places-autocomplete-svelte 2.2.23 → 2.2.24

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
@@ -50,6 +50,7 @@ The component handles API loading, session tokens, debounced fetching, and acces
50
50
  * 💰 Automatically handles **session tokens** for optimal cost management
51
51
  * ⚡ **Debounced Input:** Configurable delay to minimise API calls while typing
52
52
  * ✨ **Suggestion Highlighting:** Automatically highlights matched text in suggestions
53
+ * 🌍 **Internationally Neutral:** No default regional restrictions - works globally out of the box
53
54
 
54
55
  ### Accessibility & User Experience
55
56
  * ♿ **WCAG Compliant:** Follows WAI-ARIA patterns for comboboxes
@@ -58,7 +59,7 @@ The component handles API loading, session tokens, debounced fetching, and acces
58
59
 
59
60
  ### Developer Experience
60
61
  * 🎨 **Customisable Styling:** Override default styles via `options.classes` prop
61
- * 🔧 **Imperative API:** Direct control with `clear()`, `focus()`, `getRequestParams()`, `setRequestParams()`, `setFetchFields()`, and `getFetchFields()` methods
62
+ * 🔧 **Imperative API:** Direct control with `clear()`, `focus()`, `getRequestParams()`, `setRequestParams()`, `setFetchFields()`, `getFetchFields()`, and `setInputValue()` methods
62
63
  * 📘 **TypeScript Support:** Fully typed with comprehensive type definitions
63
64
  * 🔐 **Secure:** XSS protection with safe rendering of suggestions
64
65
  * 🎯 **Event Handling:** `onResponse` and `onError` callbacks for complete control
@@ -94,7 +95,6 @@ This component has been recognised as a winner of the **Google Maps Platform Awa
94
95
  ## Requirements
95
96
 
96
97
  * **Svelte 5+** - This component requires Svelte 5.0.0 or higher and uses Svelte 5 features including runes (`$state`, `$derived`, etc.)
97
- * **Node.js 18+** - Required for development and building
98
98
  * **Google Maps API Key** with the "Places API" enabled. Refer to [Use API Keys](https://developers.google.com/maps/documentation/javascript/get-api-key) for detailed instructions.
99
99
 
100
100
  ## Installation
@@ -122,6 +122,21 @@ For simple use cases, just pass your API key to the component. It will automatic
122
122
  // Get API Key securely (e.g., from environment variables)
123
123
  const PUBLIC_GOOGLE_MAPS_API_KEY = import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY;
124
124
 
125
+ // Optional: Set regional preferences (component works globally by default)
126
+ const requestParams = {
127
+ language: 'en-US', // Set language preference
128
+ region: 'US', // Set regional bias
129
+ // includedRegionCodes: ['US', 'CA'] // Restrict to specific regions
130
+ };
131
+
132
+ // Optional: Configure UI and behaviour
133
+ const options = {
134
+ show_place_type: true, // Show place type icons
135
+ distance: true, // Show distance (if origin provided)
136
+ response_type: 'json', // Return JSON object (default)
137
+ placeholder: 'Search for places...'
138
+ };
139
+
125
140
  const handleResponse = (response: PlaceResult) => {
126
141
  console.log('Selected:', response.formattedAddress);
127
142
  };
@@ -133,6 +148,8 @@ For simple use cases, just pass your API key to the component. It will automatic
133
148
 
134
149
  <PlaceAutocomplete
135
150
  {PUBLIC_GOOGLE_MAPS_API_KEY}
151
+ {requestParams}
152
+ {options}
136
153
  onResponse={handleResponse}
137
154
  onError={handleError}
138
155
  />
@@ -259,8 +276,8 @@ This component is built to be accessible and follows the [WAI-ARIA Authoring Pra
259
276
  | `onResponse` | `(response: PlaceResult) => void` | Yes | - | Callback triggered when a user selects a place. Receives the full place details object. |
260
277
  | `onError` | `(error: string) => void` | Yes | - | Callback triggered when an error occurs (API loading, network issues, etc.). |
261
278
  | `fetchFields` | `string[]` | No | `['formattedAddress', 'addressComponents']` | Place Data Fields to request from the API. See [Place Data Fields](https://developers.google.com/maps/documentation/javascript/place-data-fields). **Affects API billing.** |
262
- | `requestParams` | `Partial<RequestParams>` | No | `{ inputOffset: 3 }` | Parameters for the Autocomplete API request (language, region, location bias, etc.). See RequestParams interface. |
263
- | `options` | `Partial<ComponentOptions>` | No | `{ debounce: 100 }` | UI and behavior options (placeholder, debounce delay, distance display, custom classes, etc.). See ComponentOptions interface. |
279
+ | `requestParams` | `Partial<RequestParams>` | No | `{ inputOffset: 0 }` | Parameters for the Autocomplete API request. By default, the component is internationally neutral (no default `language` or `region`), allowing the Google Maps API to use browser settings and IP-based location detection. You can specify parameters like `language`, `region` (for biasing results), or `includedRegionCodes` (for filtering results) to customize behaviour. See the RequestParams interface for all options. |
280
+ | `options` | `Partial<ComponentOptions>` | No | `{ debounce: 100 }` | UI and behaviour options (placeholder, debounce delay, distance display, custom classes, etc.). See ComponentOptions interface. |
264
281
 
265
282
  *Either `PUBLIC_GOOGLE_MAPS_API_KEY` prop OR manual initialisation with `initialiseGMaps()` is required.
266
283
 
@@ -287,6 +304,9 @@ Get a reference to the component instance using `bind:this` to call its methods
287
304
  <button onclick={() => autocompleteComponent?.setOptions({ placeholder: 'Search locations...', debounce: 300 })}>
288
305
  Update Options
289
306
  </button>
307
+ <button onclick={() => autocompleteComponent?.setInputValue(48.8584, 2.2945)}>
308
+ Set to Eiffel Tower
309
+ </button>
290
310
  ```
291
311
 
292
312
  | Method | Signature | Description |
@@ -299,6 +319,94 @@ Get a reference to the component instance using `bind:this` to call its methods
299
319
  | `getFetchFields()` | `() => string[]` | Returns the current array of Place Data Fields that will be requested. |
300
320
  | `setOptions(options)` | `(options: Partial<ComponentOptions>) => void` | Dynamically updates the component's configuration options. Merges the provided options with existing settings. |
301
321
  | `getOptions()` | `() => ComponentOptions` | Returns the current validated options used by the component. Useful for inspecting configuration settings. |
322
+ | `setInputValue(latitude, longitude)` | `(latitude: number, longitude: number) => Promise<void>` | Sets the input by finding and selecting a place for the given coordinates. Performs reverse geocoding to convert lat/lng to a place, then triggers `onResponse`. **Requires Geocoding API to be enabled.** |
323
+
324
+ ### Reverse Geocoding with `setInputValue`
325
+
326
+ The `setInputValue` method allows you to programmatically set a location by coordinates, useful for integrating with geolocation APIs or map click events:
327
+
328
+ ```javascript
329
+ // Example: Set location from user's current position
330
+ navigator.geolocation.getCurrentPosition(async (position) => {
331
+ try {
332
+ await autocompleteComponent.setInputValue(
333
+ position.coords.latitude,
334
+ position.coords.longitude
335
+ );
336
+ console.log('Location set successfully');
337
+ } catch (error) {
338
+ console.error('Failed to set location:', error);
339
+ }
340
+ });
341
+
342
+ // Example: Set specific landmark (Eiffel Tower)
343
+ await autocompleteComponent.setInputValue(48.8584, 2.2945);
344
+ ```
345
+
346
+ **Important:** This method requires the **Geocoding API** to be enabled in your Google Cloud Console project. The method:
347
+ 1. Performs reverse geocoding to convert coordinates to a place
348
+ 2. Fetches place details using your configured `fetchFields`
349
+ 3. Triggers the `onResponse` callback with the place data
350
+ 4. Updates the input field (respects `clear_input` option)
351
+
352
+ ## Advanced Response Handling
353
+
354
+ ### Response Types
355
+
356
+ Control the format of data returned by the `onResponse` callback using the `response_type` option:
357
+
358
+ **JSON Format (Default):**
359
+ ```javascript
360
+ const options = {
361
+ response_type: 'json' // Default
362
+ };
363
+
364
+ const handleResponse = (response: PlaceResult) => {
365
+ console.log(response.formattedAddress); // "123 Main St, City, Country"
366
+ console.log(response.location); // { lat: 40.7128, lng: -74.0060 }
367
+ console.log(response.addressComponents); // Array of address components
368
+ };
369
+ ```
370
+
371
+ **Google Maps Place Instance:**
372
+ ```javascript
373
+ const options = {
374
+ response_type: 'place'
375
+ };
376
+
377
+ const handleResponse = (place: google.maps.places.Place) => {
378
+ // Access to full Place API methods
379
+ console.log(place.formattedAddress);
380
+ console.log(place.location);
381
+
382
+ // Access photos (if fetchFields includes 'photos')
383
+ const photos = place.photos;
384
+ if (photos && photos.length > 0) {
385
+ const photoUrl = photos[0].getURI({ maxHeight: 1200 });
386
+ console.log('Photo URL:', photoUrl);
387
+ }
388
+
389
+ // Convert to JSON when needed
390
+ const jsonData = place.toJSON();
391
+ console.log('JSON format:', jsonData);
392
+ };
393
+ ```
394
+
395
+ ### Place Type Icons
396
+
397
+ Enable visual categorisation of suggestions with place type icons:
398
+
399
+ ```javascript
400
+ const options = {
401
+ show_place_type: true,
402
+ distance: false, // Must be false when using place type icons
403
+ fetchFields: ['formattedAddress', 'primaryType'] // Ensure primaryType is included
404
+ };
405
+ ```
406
+
407
+ **Important:** `show_place_type` and `distance` are mutually exclusive - only one can be enabled at a time. When `show_place_type` is `true`, the distance display is automatically disabled.
408
+
409
+ This displays categorized icons (🏪 Shopping, 🍽️ Dining, 🏨 Lodging, etc.) on the right side of each suggestion, helping users quickly identify the type of place.
302
410
 
303
411
  ## Options
304
412
 
@@ -306,13 +414,15 @@ Get a reference to the component instance using `bind:this` to call its methods
306
414
  | :--- | :--- | :--- | :--- |
307
415
  | `placeholder` | `string` | `''` | Placeholder text for the input field. |
308
416
  | `debounce` | `number` | `100` | Delay in ms before firing API request. Set to `0` to disable. |
309
- | `distance` | `boolean` | `true` | Show distance from `requestParams.origin` (if provided). |
417
+ | `distance` | `boolean` | `false` | Show distance from `requestParams.origin` (if provided). **Mutually exclusive with `show_place_type`.** |
310
418
  | `distance_units` | `'km' \| 'miles'` | `'km'` | Units for displaying distance. |
311
419
  | `label` | `string` | `''` | Optional label text displayed above the input. |
312
420
  | `autofocus` | `boolean` | `false` | Automatically focus the input on mount. |
313
421
  | `autocomplete` | `string` | `'off'` | The `autocomplete` attribute for the input field. |
314
422
  | `classes` | `Partial<ComponentClasses>` | `{}` | Object to override default CSS classes. See Styling section. |
315
423
  | `clear_input` | `boolean` | `true` | If `false`, retains the `formattedAddress` in the input after selection. |
424
+ | `response_type` | `'json' \| 'place'` | `'json'` | Return format: `'json'` for JSON object (`.toJSON()`), `'place'` for Google Maps Place instance with access to methods like `.getPhotos()`. |
425
+ | `show_place_type` | `boolean` | `false` | Display place type icons (shopping, dining, etc.) on the right side of suggestion items. **Mutually exclusive with `distance`.** |
316
426
 
317
427
  ## Styling
318
428
 
@@ -322,14 +432,14 @@ The component includes built-in styles with `.pac-` prefixed CSS classes, provid
322
432
 
323
433
  * **Framework-agnostic**: Pure CSS with no dependencies on Tailwind or other frameworks
324
434
  * **Modern design**: Clean, professional appearance with proper spacing, shadows, and hover effects
325
- * **Fully functional**: Includes keyboard navigation indicators, loading states, and responsive behavior
435
+ * **Fully functional**: Includes keyboard navigation indicators, loading states, and responsive behaviour
326
436
  * **Customisable**: All styles can be overridden via the `options.classes` prop
327
437
 
328
438
  The default styles include:
329
439
  - Rounded input with shadow and focus states
330
- - Dropdown list with scroll behavior and dividers
440
+ - Dropdown list with scroll behaviour and dividers
331
441
  - Keyboard navigation hints (Esc, ↑, ↓)
332
- - Highlighted current selection with color transitions
442
+ - Highlighted current selection with colour transitions
333
443
  - Distance display for location-based results
334
444
  - Icon support with proper alignment
335
445
  - Responsive layout for mobile and desktop
@@ -358,7 +468,10 @@ Override any default styling by providing your own CSS classes via `options.clas
358
468
  * `li_div_p_container`: Container for paragraphs (default: `.pac-li-div-p-container`)
359
469
  * `li_div_two`: Second inner `div` containing the distance (default: `.pac-li-div-two`)
360
470
  * `li_div_two_p`: The `<p>` tag containing the distance text (default: `.pac-li-div-two-p`)
361
- * `kbd_container`: Container for the keyboard hint keys (default: `.pac-kbd-container`)
471
+ * `li_div_two_p_place_type`: Container for place type display (default: `.pac-li-div-two-p-place_type`)
472
+ * `li_div_two_p_place_type_icon`: The place type icon element (default: `.pac-li-div-two-p-place_type-icon`)
473
+ * `li_div_two_p_place_type_label`: The place type label text (default: `.pac-li-div-two-p-place_type-label`)
474
+ * `kbd_container`: Container for the keyboard hint keys (default: `.pac-kbd-container`))
362
475
  * `kbd_escape`: The `<kbd>` tag for the 'Esc' hint (default: `.pac-kbd-escape`)
363
476
  * `kbd_up`: The `<kbd>` tag for the 'Up Arrow' hint (default: `.pac-kbd-up`)
364
477
  * `kbd_down`: The `<kbd>` tag for the 'Down Arrow' hint (default: `.pac-kbd-down`)
@@ -375,7 +488,11 @@ const options = {
375
488
  ul: 'absolute mt-1 w-full bg-white shadow-lg rounded-md border border-gray-200 max-h-60 overflow-auto',
376
489
  li: 'px-4 py-2 hover:bg-blue-500 hover:text-white cursor-pointer',
377
490
  li_current: 'bg-blue-500 text-white',
378
- highlight: 'font-semibold text-blue-700'
491
+ highlight: 'font-semibold text-blue-700',
492
+ // Place type styling
493
+ li_div_two_p_place_type: 'flex items-center gap-1 text-gray-500',
494
+ li_div_two_p_place_type_icon: 'w-4 h-4',
495
+ li_div_two_p_place_type_label: 'text-xs font-medium'
379
496
  }
380
497
  };
381
498
  ```
@@ -416,7 +533,7 @@ import { PlaceAutocomplete } from 'places-autocomplete-svelte';
416
533
  ```typescript
417
534
  import type {
418
535
  PlaceResult, // Place data returned from API
419
- ComponentOptions, // UI and behavior configuration
536
+ ComponentOptions, // UI and behaviour configuration
420
537
  RequestParams, // Autocomplete request parameters
421
538
  FormattedAddress, // Standardised address structure
422
539
  ComponentClasses, // CSS class overrides
@@ -445,6 +562,34 @@ import {
445
562
  * Place Details requests (via `fetchFields`) are billed separately. **Only request the fields you need** to manage costs effectively.
446
563
  * For detailed pricing information, see [Google Maps Platform Pricing](https://developers.google.com/maps/documentation/javascript/usage-and-billing).
447
564
 
565
+ ## Troubleshooting
566
+
567
+ Common issues and how to resolve them:
568
+
569
+ ### No Suggestions Appear / "This API project is not authorised..."
570
+
571
+ This is typically an issue with your Google Maps API key.
572
+
573
+ * **Check API Key Restrictions:** Ensure your key is correctly restricted. For web use, you should have an **HTTP referrer** restriction matching your website's domain (e.g., `yourdomain.com/*`). For local development, you might need to add `localhost:*` or your specific local server address.
574
+ * **Enable APIs:** In the Google Cloud Console, make sure you have enabled both the **Places API** and the **Maps JavaScript API** for your project. The `setInputValue()` method also requires the **Geocoding API**.
575
+ * **Enable Billing:** Your Google Cloud project must have a valid billing account attached.
576
+ * **Check for Errors:** Open your browser's developer console and check for any error messages from the Google Maps script.
577
+
578
+ ### "Loader must not be called again" Error
579
+
580
+ This error occurs when you have multiple Google Maps components on the same page (or across different routes in a SvelteKit app) and each one tries to initialize the Google Maps loader independently.
581
+
582
+ **Solution:** Use the **Advanced Usage (Manual Initialisation)** pattern.
583
+ 1. Call `setGMapsContext()` once in a shared parent component (like `+layout.svelte`).
584
+ 2. Call `initialiseGMaps()` once in the same parent component, inside a `if (browser)` block.
585
+ 3. The `PlaceAutocomplete` component (and any other Google Maps components) will then automatically use the shared loader instance, preventing the error.
586
+
587
+ ### Component or Styles Not Loading Correctly
588
+
589
+ * **Check Installation:** Make sure the `places-autocomplete-svelte` package is correctly installed in your `node_modules`.
590
+ * **SvelteKit / Vite Config:** Ensure your bundler is correctly processing Svelte components from `node_modules`. This is usually handled automatically.
591
+ * **CSS Conflicts:** If the styling looks broken, you may have global styles that are conflicting with the component's default `.pac-` classes. You can either debug the conflicting styles or use the `options.classes` prop to replace the default classes with your own (e.g., Tailwind CSS classes), giving you full control over styling.
592
+
448
593
  ## Standalone JavaScript Library
449
594
 
450
595
  Need this functionality for a non-Svelte project? Check out our companion vanilla JavaScript library:
@@ -5,10 +5,10 @@
5
5
  * @see https://github.com/alexpechkarev/places-autocomplete-svelte
6
6
  * @version 1.0.0
7
7
  * @license MIT
8
- *
8
+ *
9
9
  * A production-ready, accessible autocomplete component for Google Places API (New).
10
10
  * Built with Svelte 5 runes for optimal reactivity and performance.
11
- *
11
+ *
12
12
  * @features
13
13
  * - Full keyboard navigation (ArrowUp, ArrowDown, Enter, Escape)
14
14
  * - Session token management for billing optimization
@@ -18,8 +18,8 @@
18
18
  * - Text highlighting in suggestions
19
19
  * - ARIA-compliant accessibility
20
20
  * - Imperative API for programmatic control
21
- *
22
- *
21
+ *
22
+ *
23
23
  * @publicMethods
24
24
  * - `clear()` - Clears input and resets session
25
25
  * - `focus()` - Focuses the input field
@@ -27,7 +27,8 @@
27
27
  * - `setRequestParams(params)` - Updates request parameters dynamically
28
28
  * - `setFetchFields(fields)` - Updates Place Data Fields to fetch
29
29
  * - `getFetchFields()` - Returns current fetch fields
30
- *
30
+ * - `setInputValue(latitude, longitude)` - Finds and selects a place by coordinates
31
+ *
31
32
  */
32
33
 
33
34
  import { onMount, untrack } from 'svelte';
@@ -45,7 +46,9 @@
45
46
  formatDistance,
46
47
  validateFetchFields,
47
48
  createHighlightedSegments,
48
- debounce
49
+ debounce,
50
+ ITINERARY_CATEGORIES,
51
+ ITINERARY_SVG_ICONS
49
52
  } from './helpers.js';
50
53
 
51
54
  let gmaps: GMapsContext | undefined;
@@ -116,7 +119,7 @@
116
119
  * @param {PlaceResult} [placeData] - Optional place data to populate the input field with.
117
120
  * @private
118
121
  */
119
- const reset = (placeData?: PlaceResult) => {
122
+ export const reset = (placeData?: PlaceResult) => {
120
123
  currentSuggestion = -1;
121
124
  if (validatedOptions?.clear_input == false) {
122
125
  if (placeData && placeData.formattedAddress) {
@@ -227,10 +230,9 @@
227
230
  * showDistance: true
228
231
  * });
229
232
  */
230
- export function setOptions(options: typeof validatedOptions){
233
+ export function setOptions(options: typeof validatedOptions) {
231
234
  validatedOptions = validateOptions(options);
232
- }
233
-
235
+ }
234
236
 
235
237
  /**
236
238
  * Returns the current validated options used by the component.
@@ -245,6 +247,71 @@
245
247
  return validatedOptions;
246
248
  }
247
249
 
250
+ /**
251
+ * Sets the input value by finding and selecting a place for the given coordinates.
252
+ * Performs reverse geocoding to convert latitude/longitude to a place, then fetches
253
+ * place details and triggers the onResponse callback.
254
+ * @public
255
+ * @param {number} latitude - The latitude coordinate of the location.
256
+ * @param {number} longitude - The longitude coordinate of the location.
257
+ * @returns {Promise<void>} A promise that resolves when the place has been found and selected.
258
+ * @throws {Error} If the geocoding fails or no place is found for the coordinates.
259
+ * @example
260
+ * // Set input to a specific location (e.g., Eiffel Tower)
261
+ * await autocompleteComponent.setInputValue(48.8584, 2.2945);
262
+ */
263
+ export async function setInputValue(latitude: number, longitude: number): Promise<void> {
264
+ try {
265
+ // Ensure placesApi is loaded
266
+ if (!placesApi.AutocompleteSuggestion) {
267
+ throw new Error('Places API not loaded yet. Please wait for component initialisation.');
268
+ }
269
+
270
+ // Import the geocoding library
271
+ const { Geocoder } = await importLibrary('geocoding');
272
+ const geocoder = new Geocoder();
273
+
274
+ // Perform reverse geocoding
275
+ const response = await geocoder.geocode({
276
+ location: { lat: latitude, lng: longitude }
277
+ });
278
+
279
+ if (!response.results || response.results.length === 0) {
280
+ throw new Error('No place found for the given coordinates.');
281
+ }
282
+
283
+ // Get the first result (most specific)
284
+ const geocodeResult = response.results[0];
285
+
286
+ // Import the Place class to create a Place object from the place ID
287
+ const { Place } = await importLibrary('places');
288
+ const place = new Place({
289
+ id: geocodeResult.place_id
290
+ });
291
+
292
+ // Fetch the place details with the configured fields
293
+ await place.fetchFields({
294
+ fields: validatedFetchFields
295
+ });
296
+
297
+ // Convert to JSON format
298
+ const placeData = place.toJSON() as PlaceResult;
299
+
300
+ // Reset search input and results
301
+ reset(placeData);
302
+
303
+ // Trigger the onResponse callback
304
+ onResponse(placeData);
305
+ } catch (e: any) {
306
+ // Handle errors
307
+ onError(
308
+ (e.name || 'An error occurred') +
309
+ ' - ' +
310
+ (e.message || 'error setting input value from coordinates')
311
+ );
312
+ throw e;
313
+ }
314
+ }
248
315
 
249
316
  /**
250
317
  * Extracts a specific address component from the place response.
@@ -329,7 +396,35 @@
329
396
 
330
397
  // Create highlighted segments
331
398
  highlightedText = createHighlightedSegments(originalText, matches);
399
+ //console.log(suggestion.placePrediction.types);
400
+
401
+ // Extract the types array for cleaner code
402
+ const types = suggestion.placePrediction.types;
403
+ let placeType = {
404
+ icon: '',
405
+ label: ''
406
+ };
332
407
 
408
+ if (validatedOptions.show_place_type) {
409
+ if (Array.isArray(types) && types.length > 0) {
410
+ // Look through the array until we find a type we actually recognize
411
+ const matchedType = types.find(
412
+ (type: string) => typeof type === 'string' && type in ITINERARY_CATEGORIES
413
+ );
414
+
415
+ // If we found a match, get its label; otherwise, use 'Default'
416
+ const categoryLabel = matchedType
417
+ ? ITINERARY_CATEGORIES[matchedType as keyof typeof ITINERARY_CATEGORIES]
418
+ : 'Default';
419
+
420
+ placeType = {
421
+ icon: ITINERARY_SVG_ICONS[categoryLabel as keyof typeof ITINERARY_SVG_ICONS] || '',
422
+ label: categoryLabel
423
+ };
424
+ }
425
+ }
426
+
427
+ // const category = ITINERARY_CATEGORIES[suggestion.placePrediction.placeType] || 'Default';
333
428
  results.push({
334
429
  place: place,
335
430
  mainText: highlightedText,
@@ -337,7 +432,8 @@
337
432
  distance: formatDistance(
338
433
  suggestion.placePrediction.distanceMeters,
339
434
  validatedOptions.distance_units ?? 'km'
340
- )
435
+ ),
436
+ placeType: placeType
341
437
  });
342
438
  }
343
439
  //console.log('Autocomplete suggestions:', results);
@@ -351,7 +447,9 @@
351
447
  * The debounce delay is reactive and updates when the validatedOptions.debounce value changes.
352
448
  * @private
353
449
  */
354
- const debouncedMakeAcRequest = $derived(debounce(makeAcRequest, validatedOptions?.debounce ?? 100));
450
+ const debouncedMakeAcRequest = $derived(
451
+ debounce(makeAcRequest, validatedOptions?.debounce ?? 100)
452
+ );
355
453
 
356
454
  /**
357
455
  * Handles the selection of an autocomplete suggestion.
@@ -366,12 +464,11 @@
366
464
  toJSON: () => any;
367
465
  }): Promise<void> => {
368
466
  try {
369
- // console.log(place);
370
- // console.log(validatedFetchFields);
371
467
  await place.fetchFields({
372
468
  fields: validatedFetchFields
373
469
  });
374
- let placeData = place.toJSON();
470
+ // return place as json or full place class instance
471
+ let placeData = validatedOptions.response_type === 'json' ? place.toJSON() : place;
375
472
  // reset search input and results
376
473
  reset(placeData);
377
474
  onResponse(placeData);
@@ -406,7 +503,7 @@
406
503
  onMount(async (): Promise<void> => {
407
504
  if (isDefaultOnResponse) {
408
505
  console.warn(
409
- 'PlaceAutocomplete: The `onResponse` callback has not been provided. Selected place data will not be handled. See documentation for usage.'
506
+ '[PlaceAutocomplete] No onResponse callback provided. Place selection events will not be handled. Please provide an onResponse function to handle place selections. See: https://places-autocomplete-svelte.uk/docs'
410
507
  );
411
508
  }
412
509
  if (validatedOptions.autofocus) {
@@ -593,7 +690,7 @@
593
690
  stroke-width="2"
594
691
  stroke-linecap="round"
595
692
  stroke-linejoin="round"
596
- class="size-5">{@html validatedOptions.classes.map_pin_icon}</svg
693
+ class="size-5 shrink-0">{@html validatedOptions.classes.map_pin_icon}</svg
597
694
  >
598
695
  {/if}
599
696
 
@@ -625,7 +722,7 @@
625
722
  </div>
626
723
  </div>
627
724
  </div>
628
- {#if validatedOptions.distance && p.distance}
725
+ {#if validatedOptions.distance && p.distance && !validatedOptions.show_place_type}
629
726
  <div class={[validatedOptions.classes?.li_div_two]}>
630
727
  <p
631
728
  class={[
@@ -637,6 +734,33 @@
637
734
  </p>
638
735
  </div>
639
736
  {/if}
737
+ {#if validatedOptions.show_place_type && p.placeType && !validatedOptions.distance}
738
+ <div class={[validatedOptions.classes?.li_div_two]}>
739
+ <div
740
+ class={[
741
+ i === currentSuggestion && validatedOptions.classes?.li_current,
742
+ validatedOptions.classes?.li_div_two_p_place_type
743
+ ]}
744
+ >
745
+ <div
746
+ class={[
747
+ i === currentSuggestion && validatedOptions.classes?.li_current,
748
+ validatedOptions.classes?.li_div_two_p_place_type_icon
749
+ ]}
750
+ >
751
+ {@html p.placeType.icon}
752
+ </div>
753
+ <div
754
+ class={[
755
+ i === currentSuggestion && validatedOptions.classes?.li_current,
756
+ validatedOptions.classes?.li_div_two_p_place_type_label
757
+ ]}
758
+ >
759
+ {p.placeType.label}
760
+ </div>
761
+ </div>
762
+ </div>
763
+ {/if}
640
764
  </button>
641
765
  </li>
642
766
  {/each}
@@ -689,8 +813,8 @@
689
813
  monospace;
690
814
  -webkit-text-size-adjust: 100%;
691
815
  -moz-tab-size: 4;
692
- -o-tab-size: 4;
693
- tab-size: 4;
816
+ -o-tab-size: 4;
817
+ tab-size: 4;
694
818
  line-height: 1.5;
695
819
  font-family: var(
696
820
  --default-font-family,
@@ -805,6 +929,13 @@
805
929
  --default-mono-font-family: var(--font-mono);
806
930
  --color-primary-500: #fe795d;
807
931
  }
932
+ .size-5 {
933
+ width: 20px;
934
+ height: 20px;
935
+ }
936
+ .shrink-0 {
937
+ flex-shrink: 0;
938
+ }
808
939
  .pac-section {
809
940
  width: 100%;
810
941
  }
@@ -867,6 +998,11 @@
867
998
  padding-right: calc(var(--spacing, 0.25rem) * 1.5);
868
999
  display: flex;
869
1000
  position: absolute;
1001
+ /* max-height: 40px; */
1002
+ align-content: center;
1003
+ align-items: center;
1004
+ flex-wrap: wrap;
1005
+ flex-direction: row;
870
1006
  }
871
1007
  .pac-kbd-escape {
872
1008
  margin-right: calc(var(--spacing, 0.25rem) * 1);
@@ -995,7 +1131,7 @@
995
1131
  color: var(--color-gray-900, oklch(21% 0.034 264.665));
996
1132
  -webkit-user-select: none;
997
1133
  -moz-user-select: none;
998
- user-select: none;
1134
+ user-select: none;
999
1135
  }
1000
1136
  @media (hover: hover) {
1001
1137
  .pac-li:hover {
@@ -1035,7 +1171,7 @@
1035
1171
  min-width: calc(var(--spacing, 0.25rem) * 0);
1036
1172
  align-items: center;
1037
1173
  -moz-column-gap: calc(var(--spacing, 0.25rem) * 3);
1038
- column-gap: calc(var(--spacing, 0.25rem) * 3);
1174
+ column-gap: calc(var(--spacing, 0.25rem) * 3);
1039
1175
  flex: auto;
1040
1176
  display: flex;
1041
1177
  }
@@ -1058,6 +1194,7 @@
1058
1194
  .pac-li-div-one-p-secondaryText {
1059
1195
  font-size: var(--text-xs, 0.75rem);
1060
1196
  line-height: calc(var(--spacing, 0.25rem) * 4);
1197
+ text-align: left;
1061
1198
  }
1062
1199
  .pac-li-current {
1063
1200
  background-color: var(--color-indigo-500, oklch(58.5% 0.233 277.117));
@@ -1079,6 +1216,43 @@
1079
1216
  font-size: var(--text-xs, 0.75rem);
1080
1217
  line-height: var(--tw-leading, var(--text-xs--line-height, calc(1 / 0.75)));
1081
1218
  }
1219
+ .pac-li-div-two-p-place_type {
1220
+ font-size: var(--text-xs, 0.75rem);
1221
+ line-height: var(--tw-leading, var(--text-xs--line-height, calc(1 / 0.75)));
1222
+ display: flex;
1223
+ flex-direction: row;
1224
+ align-items: center;
1225
+ /* justify-content: space-between; */
1226
+ justify-content: flex-end;
1227
+ width: 100%;
1228
+ /* min-width: 120px; */
1229
+ gap: 0.5rem;
1230
+ }
1231
+ .pac-li-div-two-p-place_type-icon {
1232
+ flex-shrink: 0;
1233
+ }
1234
+ .pac-li-div-two-p-place_type-label {
1235
+ /* text-wrap: left; */
1236
+ display: none;
1237
+ }
1238
+
1239
+ @media (min-width: 48rem) {
1240
+ .pac-li-div-two-p-place_type {
1241
+ font-size: var(--text-xs, 0.75rem);
1242
+ line-height: var(--tw-leading, var(--text-xs--line-height, calc(1 / 0.75)));
1243
+ display: flex;
1244
+ flex-direction: row;
1245
+ align-items: center;
1246
+ justify-content: flex-start;
1247
+ width: 100%;
1248
+ min-width: 120px;
1249
+ gap: 0.5rem;
1250
+ }
1251
+ .pac-li-div-two-p-place_type-label {
1252
+ /* text-wrap: left; */
1253
+ display: inline;
1254
+ }
1255
+ }
1082
1256
  .pac-highlight {
1083
1257
  --tw-font-weight: var(--font-weight-bold, 700);
1084
1258
  font-weight: var(--font-weight-bold, 700);
@@ -1,5 +1,11 @@
1
- import type { Props } from './interfaces.js';
1
+ import type { PlaceResult, Props } from './interfaces.js';
2
2
  declare const PlaceAutocomplete: import("svelte").Component<Props, {
3
+ /**
4
+ * Resets the search input and clears the suggestions list.
5
+ * Optionally populates the input with the formatted address of the selected place.
6
+ * @param {PlaceResult} [placeData] - Optional place data to populate the input field with.
7
+ * @private
8
+ */ reset: (placeData?: PlaceResult) => void;
3
9
  clear: () => void;
4
10
  focus: () => void;
5
11
  getRequestParams: () => import("./interfaces.js").RequestParams;
@@ -8,6 +14,7 @@ declare const PlaceAutocomplete: import("svelte").Component<Props, {
8
14
  getFetchFields: () => string[];
9
15
  setOptions: (options: import("./interfaces.js").ComponentOptions) => void;
10
16
  getOptions: () => import("./interfaces.js").ComponentOptions;
17
+ setInputValue: (latitude: number, longitude: number) => Promise<void>;
11
18
  }, "onResponse" | "onError">;
12
19
  type PlaceAutocomplete = ReturnType<typeof PlaceAutocomplete>;
13
20
  export default PlaceAutocomplete;
package/dist/helpers.d.ts CHANGED
@@ -63,3 +63,114 @@ export declare function createHighlightedSegments(originalText: string, matches:
63
63
  * @param delay The debounce delay in milliseconds.
64
64
  */
65
65
  export declare const debounce: <T extends (...args: any[]) => void>(func: T, delay: number) => (...args: Parameters<T>) => void;
66
+ export declare const ITINERARY_CATEGORIES: {
67
+ car_rental: string;
68
+ car_dealer: string;
69
+ gas_station: string;
70
+ electric_vehicle_charging_station: string;
71
+ parking: string;
72
+ airport: string;
73
+ bus_station: string;
74
+ train_station: string;
75
+ subway_station: string;
76
+ taxi_stand: string;
77
+ ferry_terminal: string;
78
+ restaurant: string;
79
+ cafe: string;
80
+ coffee_shop: string;
81
+ bar: string;
82
+ pub: string;
83
+ night_club: string;
84
+ bakery: string;
85
+ fast_food_restaurant: string;
86
+ ice_cream_shop: string;
87
+ pizza_restaurant: string;
88
+ steak_house: string;
89
+ sushi_restaurant: string;
90
+ hotel: string;
91
+ hostel: string;
92
+ motel: string;
93
+ resort_hotel: string;
94
+ bed_and_breakfast: string;
95
+ campground: string;
96
+ rv_park: string;
97
+ lodging: string;
98
+ cottage: string;
99
+ inn: string;
100
+ guest_house: string;
101
+ tourist_attraction: string;
102
+ museum: string;
103
+ art_gallery: string;
104
+ cultural_landmark: string;
105
+ historical_landmark: string;
106
+ monument: string;
107
+ performing_arts_theater: string;
108
+ aquarium: string;
109
+ zoo: string;
110
+ visitor_center: string;
111
+ town_square: string;
112
+ landmark: string;
113
+ place_of_worship: string;
114
+ park: string;
115
+ national_park: string;
116
+ state_park: string;
117
+ beach: string;
118
+ hiking_area: string;
119
+ amusement_park: string;
120
+ water_park: string;
121
+ botanical_garden: string;
122
+ golf_course: string;
123
+ gym: string;
124
+ natural_feature: string;
125
+ shopping_mall: string;
126
+ supermarket: string;
127
+ grocery_store: string;
128
+ clothing_store: string;
129
+ electronics_store: string;
130
+ souvenir_shop: string;
131
+ gift_shop: string;
132
+ duty_free_store: string;
133
+ hospital: string;
134
+ pharmacy: string;
135
+ atm: string;
136
+ bank: string;
137
+ post_office: string;
138
+ police: string;
139
+ neighborhood: string;
140
+ sublocality: string;
141
+ route: string;
142
+ street_address: string;
143
+ intersection: string;
144
+ locality: string;
145
+ administrative_area_level_4: string;
146
+ country: string;
147
+ administrative_area_level_1: string;
148
+ administrative_area_level_2: string;
149
+ administrative_area_level_3: string;
150
+ administrative_area_level_5: string;
151
+ sublocality_level_1: string;
152
+ sublocality_level_2: string;
153
+ sublocality_level_3: string;
154
+ sublocality_level_4: string;
155
+ sublocality_level_5: string;
156
+ default: string;
157
+ };
158
+ export declare const ITINERARY_SVG_ICONS: {
159
+ Automotive: string;
160
+ Transport: string;
161
+ "Food and Drink": string;
162
+ Lodging: string;
163
+ Sightseeing: string;
164
+ Recreation: string;
165
+ Shopping: string;
166
+ Health: string;
167
+ Finance: string;
168
+ Geographical: string;
169
+ Navigation: string;
170
+ City: string;
171
+ District: string;
172
+ Airport: string;
173
+ "Subway Station": string;
174
+ "Train Station": string;
175
+ Default: string;
176
+ };
package/dist/helpers.js CHANGED
@@ -15,7 +15,7 @@ export const requestParamsDefault = {
15
15
  * https://developers.google.com/maps/documentation/javascript/place-types
16
16
  *
17
17
  * ['postal_code','premise','street_address','route']
18
- * FY83DD 72
18
+ *
19
19
  */
20
20
  //includedPrimaryTypes: ['postal_code','premise','street_address','route'],
21
21
  includedPrimaryTypes: [],
@@ -25,7 +25,7 @@ export const requestParamsDefault = {
25
25
  * An empty set will not restrict the results.
26
26
  * If both locationRestriction and includedRegionCodes are set, the results will be located in the area of intersection.
27
27
  */
28
- includedRegionCodes: ['GB'],
28
+ includedRegionCodes: [],
29
29
  /**
30
30
  * @type number optional
31
31
  * A zero-based Unicode character offset of input indicating the cursor position in input.
@@ -192,9 +192,9 @@ export const validateRequestParams = (requestParams) => {
192
192
  const validatedParams = {
193
193
  input: String(''),
194
194
  sessionToken: String(''),
195
- includedRegionCodes: ['GB'],
196
- language: 'en-GB',
197
- region: 'GB',
195
+ //includedRegionCodes: ['GB'],
196
+ //language: 'en-GB',
197
+ //region: 'GB',
198
198
  };
199
199
  // iterate over requestParams
200
200
  for (const key in requestParams) {
@@ -315,6 +315,9 @@ export const componentClasses = {
315
315
  li_div_one_p_secondaryText: 'pac-li-div-one-p-secondaryText', //'text-xs text-left leading-2',
316
316
  li_div_two: 'pac-li-div-two', //'shrink-0 flex flex-col items-end min-w-16',
317
317
  li_div_two_p: 'pac-li-div-two-p', //'mt-1 text-xs/5',
318
+ li_div_two_p_place_type: 'pac-li-div-two-p-place_type',
319
+ li_div_two_p_place_type_icon: 'pac-li-div-two-p-place_type-icon',
320
+ li_div_two_p_place_type_label: 'pac-li-div-two-p-place_type-label',
318
321
  highlight: 'pac-highlight', //'font-bold',
319
322
  map_pin_icon: '<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/>',
320
323
  };
@@ -325,12 +328,16 @@ export const componentOptions = {
325
328
  autofocus: false,
326
329
  autocomplete: 'off',
327
330
  placeholder: 'Start typing your address',
328
- distance: true,
331
+ distance: false,
329
332
  distance_units: 'km',
330
333
  classes: componentClasses,
331
334
  label: '',
332
335
  debounce: 100,
333
- clear_input: true
336
+ clear_input: true,
337
+ // Return Value: Object a JSON object with all the requested Place properties
338
+ // or as google.maps.places.Place class instance
339
+ response_type: 'json', // 'json' | 'place',
340
+ show_place_type: false, // show place type in the autocomplete suggestions
334
341
  };
335
342
  /**
336
343
  * Validate and cast component options
@@ -415,3 +422,127 @@ export const debounce = (func, delay) => {
415
422
  }, delay);
416
423
  };
417
424
  };
425
+ // Itinerary category mapping
426
+ export const ITINERARY_CATEGORIES = {
427
+ // --- TRANSPORT & AUTO ---
428
+ "car_rental": "Automotive",
429
+ "car_dealer": "Automotive",
430
+ "gas_station": "Automotive",
431
+ "electric_vehicle_charging_station": "Automotive",
432
+ "parking": "Automotive",
433
+ "airport": "Airport",
434
+ "bus_station": "Transport",
435
+ "train_station": "Train Station",
436
+ "subway_station": "Subway Station",
437
+ "taxi_stand": "Transport",
438
+ "ferry_terminal": "Transport",
439
+ // --- DINING & NIGHTLIFE ---
440
+ "restaurant": "Food and Drink",
441
+ "cafe": "Food and Drink",
442
+ "coffee_shop": "Food and Drink",
443
+ "bar": "Food and Drink",
444
+ "pub": "Food and Drink",
445
+ "night_club": "Food and Drink",
446
+ "bakery": "Food and Drink",
447
+ "fast_food_restaurant": "Food and Drink",
448
+ "ice_cream_shop": "Food and Drink",
449
+ "pizza_restaurant": "Food and Drink",
450
+ "steak_house": "Food and Drink",
451
+ "sushi_restaurant": "Food and Drink",
452
+ // --- LODGING ---
453
+ "hotel": "Lodging",
454
+ "hostel": "Lodging",
455
+ "motel": "Lodging",
456
+ "resort_hotel": "Lodging",
457
+ "bed_and_breakfast": "Lodging",
458
+ "campground": "Lodging",
459
+ "rv_park": "Lodging",
460
+ "lodging": "Lodging",
461
+ "cottage": "Lodging",
462
+ "inn": "Lodging",
463
+ "guest_house": "Lodging",
464
+ // --- SIGHTSEEING & CULTURE ---
465
+ "tourist_attraction": "Sightseeing",
466
+ "museum": "Sightseeing",
467
+ "art_gallery": "Sightseeing",
468
+ "cultural_landmark": "Sightseeing",
469
+ "historical_landmark": "Sightseeing",
470
+ "monument": "Sightseeing",
471
+ "performing_arts_theater": "Sightseeing",
472
+ "aquarium": "Sightseeing",
473
+ "zoo": "Sightseeing",
474
+ "visitor_center": "Sightseeing",
475
+ "town_square": "Sightseeing",
476
+ "landmark": "Sightseeing",
477
+ "place_of_worship": "Sightseeing",
478
+ // --- RECREATION & PARKS ---
479
+ "park": "Recreation",
480
+ "national_park": "Recreation",
481
+ "state_park": "Recreation",
482
+ "beach": "Recreation",
483
+ "hiking_area": "Recreation",
484
+ "amusement_park": "Recreation",
485
+ "water_park": "Recreation",
486
+ "botanical_garden": "Recreation",
487
+ "golf_course": "Recreation",
488
+ "gym": "Recreation",
489
+ "natural_feature": "Recreation",
490
+ // --- SHOPPING ---
491
+ "shopping_mall": "Shopping",
492
+ "supermarket": "Shopping",
493
+ "grocery_store": "Shopping",
494
+ "clothing_store": "Shopping",
495
+ "electronics_store": "Shopping",
496
+ "souvenir_shop": "Shopping", // Simplified name
497
+ "gift_shop": "Shopping",
498
+ "duty_free_store": "Shopping",
499
+ // --- ESSENTIAL SERVICES ---
500
+ "hospital": "Health",
501
+ "pharmacy": "Health",
502
+ "atm": "Finance",
503
+ "bank": "Finance",
504
+ "post_office": "Services",
505
+ "police": "Services",
506
+ // --- GEOGRAPHICAL ---
507
+ "neighborhood": "Geographical",
508
+ "sublocality": "Geographical",
509
+ // --- NAVIGATION ---
510
+ "route": "Navigation",
511
+ "street_address": "Navigation",
512
+ "intersection": "Navigation",
513
+ // -- CITY --
514
+ 'locality': 'City',
515
+ 'administrative_area_level_4': 'City',
516
+ 'country': 'Country',
517
+ 'administrative_area_level_1': 'City',
518
+ 'administrative_area_level_2': 'City',
519
+ 'administrative_area_level_3': 'City',
520
+ 'administrative_area_level_5': 'City',
521
+ 'sublocality_level_1': 'Neighborhood',
522
+ 'sublocality_level_2': 'Neighborhood',
523
+ 'sublocality_level_3': 'Neighborhood',
524
+ 'sublocality_level_4': 'Neighborhood',
525
+ 'sublocality_level_5': 'Neighborhood',
526
+ // --- DEFAULT ---
527
+ "default": "Default"
528
+ };
529
+ // Itinerary category SVG icons
530
+ export const ITINERARY_SVG_ICONS = {
531
+ "Automotive": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2"/><circle cx="7" cy="17" r="2"/><path d="M9 17h6"/><circle cx="17" cy="17" r="2"/></svg>`,
532
+ "Transport": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>`,
533
+ "Food and Drink": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2"/><path d="M7 2v20"/><path d="M21 15V2v0a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7"/></svg>`,
534
+ "Lodging": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4v16"/><path d="M2 8h18a2 2 0 0 1 2 2v10"/><path d="M2 17h20"/><path d="M6 8v9"/></svg>`,
535
+ "Sightseeing": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-binoculars-icon lucide-binoculars"><path d="M10 10h4"/><path d="M19 7V4a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v3"/><path d="M20 21a2 2 0 0 0 2-2v-3.851c0-1.39-2-2.962-2-4.829V8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v11a2 2 0 0 0 2 2z"/><path d="M 22 16 L 2 16"/><path d="M4 21a2 2 0 0 1-2-2v-3.851c0-1.39 2-2.962 2-4.829V8a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v11a2 2 0 0 1-2 2z"/><path d="M9 7V4a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1v3"/></svg>`,
536
+ "Recreation": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-kayak-icon lucide-kayak"><path d="M18 17a1 1 0 0 0-1 1v1a2 2 0 1 0 2-2z"/><path d="M20.97 3.61a.45.45 0 0 0-.58-.58C10.2 6.6 6.6 10.2 3.03 20.39a.45.45 0 0 0 .58.58C13.8 17.4 17.4 13.8 20.97 3.61"/><path d="m6.707 6.707 10.586 10.586"/><path d="M7 5a2 2 0 1 0-2 2h1a1 1 0 0 0 1-1z"/></svg>`,
537
+ "Shopping": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>`,
538
+ "Health": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7v4"/><path d="M14 21v-3a2 2 0 0 0-4 0v3"/><path d="M14 9h-4"/><path d="M18 11h2a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h2"/><path d="M18 21V5a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16"/></svg>`,
539
+ "Finance": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg>`,
540
+ "Geographical": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m20 20-6-10.6c-.4-.7-1.5-.7-1.9 0L6 20"/><path d="M7 16h10"/><path d="M12 4a8 8 0 0 1 8 8v2a2 2 0 0 1-2 2h-1"/><path d="M7 16H6a2 2 0 0 1-2-2v-2a8 8 0 0 1 8-8"/></svg>`,
541
+ "Navigation": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-navigation-icon lucide-navigation"><polygon points="3 11 22 2 13 21 11 13 3 11"/></svg>`,
542
+ "City": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-building2-icon lucide-building-2"><path d="M10 12h4"/><path d="M10 8h4"/><path d="M14 21v-3a2 2 0 0 0-4 0v3"/><path d="M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2"/><path d="M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16"/></svg>`,
543
+ "District": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-land-plot-icon lucide-land-plot"><path d="m12 8 6-3-6-3v10"/><path d="m8 11.99-5.5 3.14a1 1 0 0 0 0 1.74l8.5 4.86a2 2 0 0 0 2 0l8.5-4.86a1 1 0 0 0 0-1.74L16 12"/><path d="m6.49 12.85 11.02 6.3"/><path d="M17.51 12.85 6.5 19.15"/></svg>`,
544
+ "Airport": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plane-icon lucide-plane"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>`,
545
+ "Subway Station": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-subway-icon lucide-subway"><path d="M12 22a10 10 0 0 0 10-10V8l-5-5H7L2 8v4a10 10 0 0 0 10 10Z"/><path d="M12 22V8"/><path d="M7 13h10"/><path d="M7 17h10"/></svg>`,
546
+ "Train Station": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-train-icon lucide-train"><path d="M2 10h20"/><path d="M2 10a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5Z"/><circle cx="7" cy="15" r="2"/><circle cx="17" cy="15" r="2"/></svg>`,
547
+ "Default": `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="display:inline-block"; viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>`
548
+ };
@@ -64,7 +64,7 @@ export interface ComponentClasses {
64
64
  * Controls browser autofill behavior for the input field.
65
65
  * @type {"on" | "off"}
66
66
  */
67
- export type AutoFill = "on" | "off" | string;
67
+ export type AutoFill = "on" | "off" | "new-password";
68
68
  /**
69
69
  * Distance measurement units for displaying place distances.
70
70
  * @type {"km" | "miles"}
@@ -93,6 +93,28 @@ export interface ComponentOptions {
93
93
  debounce?: number;
94
94
  /** Clear the input field after selecting a place. @default true */
95
95
  clear_input?: boolean;
96
+ /** Format of the place data returned in onResponse callback. @default "json" */
97
+ response_type?: 'json' | 'object';
98
+ /** Show the place type in the autocomplete suggestions. @default false */
99
+ show_place_type?: boolean;
100
+ }
101
+ /**
102
+ * Photo Class
103
+ */
104
+ export interface Photo {
105
+ authorAttributions?: {
106
+ displayName?: string;
107
+ uri?: string;
108
+ photoURI?: string;
109
+ }[];
110
+ flagContentURI?: string;
111
+ googleMapsURI?: string;
112
+ heightPx?: number;
113
+ widthPx?: number;
114
+ getURI(options?: {
115
+ maxHeight?: number;
116
+ maxWidth?: number;
117
+ }): string;
96
118
  }
97
119
  /**
98
120
  * Detailed information about a selected place returned by the Google Places API.
@@ -121,6 +143,17 @@ export interface PlaceResult {
121
143
  lat: number;
122
144
  lng: number;
123
145
  };
146
+ photos?: Photo[];
147
+ /**
148
+ * Returns the URL of the photo with the specified options.
149
+ * @param options - Options for the photo URL.
150
+ * @returns The URL of the photo.
151
+ */
152
+ getURI(options?: {
153
+ maxHeight?: number;
154
+ maxWidth?: number;
155
+ }): string;
156
+ toJSON(): PlaceResult;
124
157
  /** Additional place data fields as requested via fetchFields prop (e.g., displayName, types, photos). */
125
158
  [key: string]: unknown;
126
159
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "places-autocomplete-svelte",
3
3
  "license": "MIT",
4
- "version": "2.2.23",
4
+ "version": "2.2.24",
5
5
  "description": "A flexible, accessible, and secure Svelte component leveraging the Google Maps Places Autocomplete API (New). Winner of the Google Maps Platform Awards 2025, recognising excellence in Google Maps Platform development.",
6
6
  "keywords": [
7
7
  "svelte",
@@ -76,8 +76,8 @@
76
76
  },
77
77
  "devDependencies": {
78
78
  "@sveltejs/adapter-auto": "^7.0.0",
79
- "@sveltejs/adapter-cloudflare": "^7.2.4",
80
- "@sveltejs/kit": "^2.49.4",
79
+ "@sveltejs/adapter-cloudflare": "^7.2.6",
80
+ "@sveltejs/kit": "^2.50.2",
81
81
  "@sveltejs/package": "^2.5.7",
82
82
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
83
83
  "@tailwindcss/postcss": "^4.1.18",
@@ -85,23 +85,24 @@
85
85
  "@tailwindcss/vite": "^4.1.18",
86
86
  "@types/eslint": "^9.6.1",
87
87
  "@types/google.maps": "^3.58.1",
88
- "@types/node": "^25.0.6",
89
- "autoprefixer": "^10.4.23",
88
+ "@types/node": "^25.2.0",
89
+ "autoprefixer": "^10.4.24",
90
90
  "eslint": "^9.39.2",
91
91
  "eslint-config-prettier": "^10.1.8",
92
92
  "eslint-plugin-svelte": "^3.14.0",
93
- "globals": "^17.0.0",
93
+ "globals": "^17.3.0",
94
94
  "postcss": "^8.5.6",
95
- "prettier": "^3.7.4",
95
+ "prettier": "^3.8.1",
96
96
  "prettier-plugin-svelte": "^3.4.1",
97
- "publint": "^0.3.16",
98
- "svelte": "^5.46.1",
99
- "svelte-check": "^4.3.5",
97
+ "publint": "^0.3.17",
98
+ "svelte": "^5.49.1",
99
+ "svelte-check": "^4.3.6",
100
100
  "tailwindcss": "^4.1.18",
101
101
  "tslib": "^2.8.1",
102
102
  "typescript": "^5.9.3",
103
- "typescript-eslint": "^8.52.0",
104
- "vite": "^7.3.1"
103
+ "typescript-eslint": "^8.54.0",
104
+ "vite": "^7.3.1",
105
+ "vitest": "^4.0.18"
105
106
  },
106
107
  "svelte": "./dist/index.js",
107
108
  "types": "./dist/PlaceAutocomplete.svelte.d.ts",