places-autocomplete-svelte 2.1.6 → 2.1.8

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
@@ -1,29 +1,34 @@
1
1
  # Places (New) Autocomplete Svelte
2
2
 
3
- This Svelte component leverages 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. Default styling is provided using [Tailwind CSS](https://tailwindcss.com/), but you can fully customize the appearance with your own styles.
3
+ This Svelte component provides a user-friendly way to search for and retrieve detailed address information within your [SvelteKit](https://kit.svelte.dev) applications, leveraging the power of the [Google Maps Places (New) Autocomplete API](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview). It comes with default styling using [Tailwind CSS](https://tailwindcss.com/), which you can fully customize.
4
4
 
5
5
 
6
6
 
7
- ## Features:
7
+ ## Features
8
8
 
9
- - **Seamless Integration:** Easily integrate the component into your SvelteKit projects.
10
- - **Autocomplete Suggestions:** Provides real-time address suggestions as the user types.
11
- - **Detailed Address Retrieval:** Retrieve comprehensive address information, including street address, city, region, postal code, and country.
12
- - **Country/Region Filtering:** Refine search results by specifying countries or regions.
13
- - **Customizable Appearance:** Tailor the component's look and feel with custom CSS classes, overriding the default Tailwind CSS styles.
14
- - **Flexible Data Retrieval:** Control the retrieved data using the `fetchFields` property.
15
- - **Accessible:** Supports keyboard navigation for selecting suggestions.
9
+ - **Seamless SvelteKit Integration:** Easily add the component to your SvelteKit projects.
10
+ - **Real-time Autocomplete Suggestions:** As the user types, address suggestions appear dynamically.
11
+ - **Comprehensive Address Details:** Retrieve detailed information, including street address, city, state/province, postal code, country, and more.
12
+ - **Country/Region Filtering:** Narrow down search results by specifying target countries or regions.
13
+ - **Customizable Styles:** Tailor the component's appearance to match your application's design by overriding the default Tailwind CSS classes.
14
+ - **Flexible Data Control:** Choose the specific data fields you want to retrieve using the `fetchFields` property.
15
+ - **Keyboard Navigation & Accessibility:** Use keyboard navigation for selecting suggestions, ensuring accessibility for all users.
16
16
 
17
17
 
18
18
  ## Demo
19
19
 
20
- See a live demo of the component in action: [Demo](https://places-autocomplete-demo.pages.dev/)
20
+ See a live demo of the component in action: [Basic Example](https://places-autocomplete-demo.pages.dev/)
21
+
22
+ [Reactive parameters](https://places-autocomplete-demo.pages.dev/examples/reactive-parameters) - change the search criteria based on user input, like filtering by country or change results language.
23
+
24
+ [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.
25
+
21
26
 
22
27
  ![Places Autocomplete Svelte](places-autocomplete-svelte.gif)
23
28
 
24
29
  ## Requirements
25
30
 
26
- - **Google Maps API Key:** Create an 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.
31
+ - **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.
27
32
 
28
33
  ## Installation Svelte 5
29
34
 
@@ -40,15 +45,17 @@ npm i places-autocomplete-svelte@1.0.1
40
45
 
41
46
  ## Basic Usage
42
47
 
43
- 1. **Provide your Google Maps API Key:** Replace `'___YOUR_API_KEY___'` with your actual Google Maps API key.
44
- 2. **Handle the Response:** Use the `onResponse` callback to receive the selected place details.
48
+ 1. Replace `'___YOUR_API_KEY___'` with your actual **Google Maps API Key**.
49
+ 2. Use the `onResponse` callback to **handle the response**.
45
50
 
46
51
  ```svelte
47
52
  <script>
48
53
  import { PlaceAutocomplete } from 'places-autocomplete-svelte';
49
54
 
55
+ //Recommended: Store your key securely as an environment variable
50
56
  const PUBLIC_GOOGLE_MAPS_API_KEY = '___YOUR_API_KEY___';
51
57
 
58
+
52
59
  let fullResponse = $state('')
53
60
  let onResponse = (response) => {
54
61
  fullResponse = response;
@@ -61,31 +68,41 @@ let onResponse = (response) => {
61
68
  ```
62
69
 
63
70
 
71
+ ## Component Properties
72
+
73
+ | Property | Type | Description | Required | Default Value |
74
+ |--------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------|
75
+ | `PUBLIC_GOOGLE_MAPS_API_KEY` | `String` | Your Google Maps Places API Key. | Yes | |
76
+ | `onResponse` | `CustomEvent` | Dispatched when a place is selected, containing the place details in `event.detail`. | Yes | |
77
+ | `onError` | `CustomEvent` | Dispatched when an error occurs, with the error message in `event.detail`. | No | |
78
+ | `requestParams` | `Object` | Object for additional request parameters (e.g., `types`, `bounds`, `origin`, `region`, `language`). See [AutocompleteRequest](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest). | No | `{}` |
79
+ | `fetchFields` | `Array` | Array of place data fields to return. See [Supported Fields](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult) | No | `['formattedAddress', 'addressComponents']` |
80
+ | `options` | `Object` | Options for customizing the component's behavior and appearance. See "Customization" below. | No | See default values in "Customization" |
81
+
82
+
64
83
 
65
84
  ## Customization
66
- - `placeholder`: Use the placeholder property to customize the input field's placeholder text.
67
- - `autocomplete`: Use to disable the HTML `<input>` autocomplete attribute.
68
- - `requestParams` (optional [AutocompleteRequest properties](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest) ):
69
- - `language`: in which to return results. If ommited defaults to `en-GB`. [See details](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest.language)
70
- - `region`: the [CLDR two-character format](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest). Defaults to `GB`. If the countries array is provided the coutries region overwrites the `region` value in `requestParams`.
71
- - `fetchFields`: Use to control the Place response. See [types](https://developers.google.com/maps/documentation/javascript/place-class-data-fields) for details. If omitted defaults to `['formattedAddress', 'addressComponents']`
72
- - `classes`: Customize the styling by providing an object with your CSS classes. This overrides the default Tailwind CSS classes. See the example in the "Basic Usage" section for the structure of the classes object and default class names.
85
+ ### Options
86
+
87
+ | Property | Type | Description | Default Value |
88
+ |----------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
89
+ | `autofocus` | `boolean` | If `true`, the input field will be focused automatically when the component mounts. | `false` |
90
+ | `placeholder` | `String` | Placeholder text for the input field. | `"Search..."` |
91
+ | `autocomplete`| `string` | HTML `autocomplete` attribute for the input field. Set to `"off"` to disable browser autocomplete. | `"off"` |
92
+ | `show_distance`| `boolean` | If `true`, and if an `origin` is specified in `requestParams`, displays the distance to each suggestion. The distance is calculated as a geodesic in meters. | `false` |
93
+ | `classes` | `Object` | Object to override default Tailwind CSS classes.structure. | See [styling](https://places-autocomplete-demo.pages.dev/examples/styling) |
94
+
95
+ ### Styling
96
+ Customize the component's appearance by providing an object to the classes property. This object should contain key-value pairs, where the keys correspond to the component's elements and the values are your custom CSS class names. See [styling](https://places-autocomplete-demo.pages.dev/examples/styling) for details.
97
+
98
+
99
+ ### Request Parameters (requestParams)
100
+ Fine-tune the autocomplete search with the requestParams property. This property accepts an object corresponding to the AutocompleteRequest object in the Google Maps API documentation. See this [request parameters](https://places-autocomplete-demo.pages.dev/component/request-parameters) for more details. Here are some common examples:
73
101
 
74
102
  ```svelte
75
103
  <script>
76
104
  // ... other imports
77
105
 
78
- /**
79
- * @type string optional
80
- */
81
- const placeholder = 'Search...';
82
- /**
83
- * @type string optional
84
- * The <input> HTML autocomplete attribute.
85
- * default: 'off'
86
- * */
87
- const autocompete = 'off';
88
-
89
106
  /**
90
107
  * @type boolean optional
91
108
  * Boolean attribute indicating that an element should be focused on page load.
@@ -109,27 +126,14 @@ const requestParams = {
109
126
 
110
127
  /**
111
128
  * @type object optional
112
- * Component default Tailwind CSS classes
129
+ * Options
113
130
  */
114
- const classes = {
115
- section: '',
116
- container: 'relative z-10 transform rounded-xl mt-4',
117
- icon_container: 'pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3',
118
- icon: '<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>',
119
- input:
120
- 'border-1 w-full rounded-md border-0 shadow-sm bg-gray-100 px-4 py-2.5 pl-10 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 sm:text-sm',
121
- kbd_container: 'absolute inset-y-0 right-0 flex py-1.5 pr-1.5',
122
- kbd_escape:
123
- 'inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1',
124
- kbd_up:
125
- 'inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6',
126
- kbd_down:
127
- 'inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6',
128
- ul: 'absolute z-50 -mb-2 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm',
129
- li: 'z-50 cursor-default select-none py-2 pl-4 text-gray-900 hover:bg-indigo-500 hover:text-white',
130
- li_current: 'bg-indigo-500 text-white',
131
- li_a: 'block w-full'
132
- },
131
+ const options = {
132
+ autofocus: false,
133
+ autocompete: 'off',
134
+ placeholder: 'Start typing your address',
135
+ show_distance: true,
136
+ };
133
137
 
134
138
  /**
135
139
  * @type array optional
@@ -142,33 +146,19 @@ const fetchFields = ['formattedAddress', 'addressComponents'];
142
146
  {onResponse}
143
147
  {PUBLIC_GOOGLE_MAPS_API_KEY}
144
148
  {requestParams}
145
- {placeholder}
146
- {autocompete}
147
- {autofocus}
149
+ {options}
148
150
  {fetchFields}
149
- {classes}
151
+
150
152
  />
151
153
 
152
154
  ```
153
155
 
154
156
 
155
- ## Component Properties
156
- | Property | Type | Description | Required | Default Value |
157
- |--------------------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------|
158
- | `PUBLIC_GOOGLE_MAPS_API_KEY` | `String` | Your Google Maps Places API Key. | Yes | |
159
- | `onResponse` | `CustomEvent` | Dispatched when a place is selected, containing the place details. | Yes | |
160
- | `onError` | `CustomEvent` | Dispatched when an error occurs. | No | |
161
- | `placeholder` | `String` | Placeholder text for the input field. | No | `"Search..."` |
162
- | `autocomplete` | `string` | HTML `autocomplete` attribute for the input field. Set to "off" to disable browser autocomplete.
163
- | `autofocus` | `boolean` | The attribute indicating that an element should be focused on page load. | No | `false` |
164
- | `requestParams` | `Object` | Object for additional request parameters (e.g., `types`, `bounds`). See [AutocompleteRequest](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest). | No | `{}` |
165
- | `fetchFields` | `Array` | Array of place data fields to return. See [Supported Fields](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult) | No | `['formattedAddress', 'addressComponents']` |
166
- | `classes` | `Object` | Object to override default Tailwind CSS classes applied to the component's elements (input, list, etc.). See the "Basic Usage" section for structure and default class names. | No | *Default Tailwind classes* |
167
157
 
168
158
 
169
159
  ## Error Handling
170
160
 
171
- The `onError` event will be dispatched if there is an issue with the Google Maps API or the autocomplete request.
161
+ Use the `onError` event handler to gracefully manage any errors that may occur during the autocomplete process:
172
162
 
173
163
 
174
164
  ```svelte
@@ -195,7 +185,7 @@ let onError = (error: string) => {
195
185
 
196
186
  ## Contributing
197
187
 
198
- Contributions are welcome! Please open an issue or submit a pull request on the [GitHub repository](https://github.com/alexpechkarev/places-autocomplete-svelte/).
188
+ Contributions are welcome! Please open an issue or submit a pull request on the [GitHub](https://github.com/alexpechkarev/places-autocomplete-svelte/).
199
189
 
200
190
  ## License
201
191
 
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
3
  import * as GMaps from '@googlemaps/js-api-loader';
4
- import type { Props } from './interfaces.js';
5
- import { validateRequestParams } from './helpers.js';
4
+ import type { ComponentOptions, Props } from './interfaces.js';
5
+ import { validateOptions, validateRequestParams } from './helpers.js';
6
6
  const { Loader } = GMaps;
7
7
 
8
8
  let {
@@ -12,42 +12,38 @@
12
12
  */
13
13
  PUBLIC_GOOGLE_MAPS_API_KEY,
14
14
  fetchFields = $bindable(['formattedAddress', 'addressComponents']),
15
- placeholder = 'Search...',
16
- autocompete = 'off',
17
- autofocus = false,
18
- classes = {
19
- section: '',
20
- container: 'relative z-10 transform rounded-xl mt-4',
21
- icon_container: 'pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3',
22
- icon: '<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>',
23
- input:
24
- 'border-1 w-full rounded-md border-0 shadow-sm bg-gray-100 px-4 py-2.5 pl-10 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 sm:text-sm',
25
- kbd_container: 'absolute inset-y-0 right-0 flex py-1.5 pr-1.5',
26
- kbd_escape:
27
- 'inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1',
28
- kbd_up:
29
- 'inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6',
30
- kbd_down:
31
- 'inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6',
32
- ul: 'absolute z-50 -mb-2 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm',
33
- li: 'z-50 cursor-default select-none py-2 pl-4 text-gray-900 hover:bg-indigo-500 hover:text-white',
34
- li_current: 'bg-indigo-500 text-white',
35
- li_a: 'block w-full'
36
- },
15
+ options,
37
16
  onResponse = $bindable((e: Event) => {}),
38
17
  onError = $bindable((error: string) => {}),
39
- requestParams
18
+ requestParams = {}
40
19
  }: Props = $props();
41
20
 
21
+ // validate options
22
+ options = validateOptions(options);
23
+
42
24
  // set classes as state
43
- let cl = $state(classes);
25
+ let cl = $state(options.classes);
44
26
 
45
- // reset keyboard classes
46
- const resetKbdClasses = () => {
47
- cl.kbd_down = classes.kbd_down;
48
- cl.kbd_up = classes.kbd_up;
49
- }
27
+ // format meters to km and meters
28
+ const formatMeters = function (meters: number): string|null {
29
+ if(typeof meters !== 'number') {
30
+ return null;
31
+ }
32
+ const km = Math.floor(meters / 1000);
33
+ const remainingMeters = meters % 1000;
34
+ let formattedString = '';
35
+ if (km > 0) {
36
+ formattedString += km + 'km ';
37
+ }
38
+ formattedString += remainingMeters + 'm';
39
+ return formattedString;
40
+ };
50
41
 
42
+ // reset keyboard classes
43
+ const resetKbdClasses = () => {
44
+ cl.kbd_down = options.classes.kbd_down;
45
+ cl.kbd_up = options.classes.kbd_up;
46
+ };
51
47
 
52
48
  // Local variables
53
49
  let inputRef: HTMLInputElement;
@@ -55,12 +51,14 @@
55
51
  let results: any[] = $state([]);
56
52
  let loader: GMaps.Loader;
57
53
  let placesApi: { [key: string]: any } = {};
54
+
55
+
58
56
  //https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data
59
57
  // validate and merge requestParams with requestParamsDefault
60
58
  //let request = $state(validateRequestParams(Object.assign(requestParamsDefault, requestParams)));
61
59
  requestParams = validateRequestParams(requestParams);
62
60
  let request = $state(requestParams);
63
-
61
+ //$inspect(request);
64
62
  // clear result when input is empty
65
63
  $effect(() => {
66
64
  if (request.input == '') {
@@ -108,12 +106,15 @@
108
106
  const { suggestions } =
109
107
  await placesApi.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
110
108
  results = [];
109
+ const formatter = new Intl.NumberFormat('en');
111
110
  // iterate suggestions and add results to an array
112
111
  for (const suggestion of suggestions) {
112
+
113
113
  // add suggestions to results
114
114
  results.push({
115
115
  to_pace: suggestion.placePrediction.toPlace(),
116
- text: suggestion.placePrediction.text.toString()
116
+ text: suggestion.placePrediction.text.toString(),
117
+ distance: formatMeters(suggestion.placePrediction.distanceMeters)
117
118
  });
118
119
  }
119
120
  } catch (e: any) {
@@ -162,7 +163,7 @@
162
163
  * Initialize the Google Maps JavaScript API Loader.
163
164
  */
164
165
  onMount(async (): Promise<void> => {
165
- if(autofocus) {
166
+ if (options.autofocus) {
166
167
  // focus on the input
167
168
  inputRef.focus();
168
169
  }
@@ -193,11 +194,11 @@
193
194
  if (e.key === 'ArrowDown') {
194
195
  currentSuggestion = Math.min(currentSuggestion + 1, results.length - 1);
195
196
  resetKbdClasses();
196
- cl.kbd_down += ' bg-indigo-500 text-white';
197
+ cl.kbd_down += ' bg-indigo-500 text-white';
197
198
  } else if (e.key === 'ArrowUp') {
198
199
  currentSuggestion = Math.max(currentSuggestion - 1, 0);
199
200
  resetKbdClasses();
200
- cl.kbd_up += ' bg-indigo-500 text-white';
201
+ cl.kbd_up += ' bg-indigo-500 text-white';
201
202
  } else if (e.key === 'Enter') {
202
203
  e.preventDefault();
203
204
  if (currentSuggestion >= 0) {
@@ -209,28 +210,29 @@
209
210
  }
210
211
 
211
212
  setTimeout(() => {
212
- resetKbdClasses();
213
- }, 300);
213
+ resetKbdClasses();
214
+ }, 300);
214
215
  }
215
216
  </script>
216
217
 
217
218
  <svelte:window onkeydown={onKeyDown} />
218
219
 
219
- <section class="{classes?.section}">
220
- <div class="{classes.container}">
221
- {#if classes.icon}
222
- <div class="{classes.icon_container}">
223
- {@html classes.icon}
224
- </div>
225
- {/if}
220
+ <section class={options.classes?.section}>
221
+ <div class={options.classes.container}>
222
+ {#if options.classes.icon}
223
+ <div class={options.classes.icon_container}>
224
+ {@html options.classes.icon}
225
+ </div>
226
+ {/if}
227
+
226
228
 
227
229
  <input
228
230
  type="text"
229
231
  name="search"
230
232
  bind:this={inputRef}
231
- class="{classes.input}"
232
- {placeholder}
233
- autocomplete={autocompete}
233
+ class={options.classes.input}
234
+ placeholder={options.placeholder}
235
+ autocomplete={options.autocomplete}
234
236
  aria-controls="options"
235
237
  aria-autocomplete="list"
236
238
  aria-owns="options"
@@ -242,37 +244,43 @@
242
244
  />
243
245
 
244
246
  {#if results.length > 0}
245
- <div class="{classes.kbd_container}">
246
- <kbd
247
- class="{classes.kbd_escape}"
248
- >Esc</kbd
249
- >
250
- <kbd
251
- class="{cl.kbd_up}"
252
- >&uArr;</kbd
253
- >
254
- <kbd
255
- class="{cl.kbd_down}"
256
- >&dArr;</kbd
257
- >
258
- </div>
259
- <ul
260
- class="{classes.ul}"
261
- id="options"
262
- >
247
+ <div class={options.classes.kbd_container}>
248
+ <kbd class={options.classes.kbd_escape}>Esc</kbd>
249
+ <kbd class={cl.kbd_up}>&uArr;</kbd>
250
+ <kbd class={cl.kbd_down}>&dArr;</kbd>
251
+ </div>
252
+
253
+ <ul class={options.classes.ul} id="options">
263
254
  {#each results as place, i}
264
255
  <li
265
- class={[ classes.li, i === currentSuggestion && classes.li_current ]}
256
+ class={[options.classes.li, i === currentSuggestion && options.classes.li_current]}
266
257
  id="option-{i + 1}"
267
258
  >
268
259
  <!-- svelte-ignore a11y_invalid_attribute -->
269
260
  <a
270
261
  href="javascript:void(0)"
271
- class="{classes?.li_a}"
262
+ class={[options.classes?.li_a, 'flex justify-between']}
272
263
  tabindex={i + 1}
273
264
  onclick={() => onPlaceSelected(place.to_pace)}
274
265
  >
275
- {place.text}
266
+ <div class="flex min-w-0 gap-x-4">
267
+ <!-- <img
268
+ class="size-12 flex-none rounded-full bg-gray-50"
269
+ src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
270
+ alt=""
271
+ /> -->
272
+ <div class="min-w-0 flex-auto">
273
+ <p class={[i === currentSuggestion && options.classes.li_current,'text-sm/6 font-semibold text-gray-900']}>{place.text}</p>
274
+ <!-- <p class="mt-1 truncate text-xs/5 text-gray-500">leslie.alexander@example.com</p> -->
275
+ </div>
276
+ </div>
277
+ {#if options.show_distance && place.distance}
278
+ <div class="shrink-0 flex flex-col items-end min-w-16">
279
+ <p class={[i === currentSuggestion && options.classes.li_current,'mt-1 text-xs/5 text-gray-500']}>
280
+ {place.distance}
281
+ </p>
282
+ </div>
283
+ {/if}
276
284
  </a>
277
285
  </li>
278
286
  {/each}
package/dist/helpers.d.ts CHANGED
@@ -1,7 +1,17 @@
1
- import type { RequestParams } from './interfaces.js';
1
+ import type { RequestParams, ComponentOptions, ComponentClasses } from './interfaces.js';
2
2
  export declare const requestParamsDefault: RequestParams;
3
3
  /**
4
4
  * Validate and cast request parameters
5
5
  * @param requestParams
6
6
  */
7
7
  export declare const validateRequestParams: (requestParams: RequestParams | undefined) => RequestParams;
8
+ /**
9
+ * Default component classes
10
+ */
11
+ export declare const componentClasses: ComponentClasses;
12
+ export declare const componentOptions: ComponentOptions;
13
+ /**
14
+ * Validate and cast component options
15
+ * @param options
16
+ */
17
+ export declare const validateOptions: (options: ComponentOptions | undefined) => ComponentOptions;
package/dist/helpers.js CHANGED
@@ -101,7 +101,7 @@ export const validateRequestParams = (requestParams) => {
101
101
  /**
102
102
  * If requestParams is not an object, set it to an empty object
103
103
  */
104
- if (typeof requestParams !== 'object') {
104
+ if (typeof requestParams !== 'object' || Object.keys(requestParams).length === 0) {
105
105
  requestParams = {
106
106
  input: String(''),
107
107
  sessionToken: String(''),
@@ -186,3 +186,58 @@ export const validateRequestParams = (requestParams) => {
186
186
  //console.log('requestParams:', Object.keys(requestParams));
187
187
  return requestParams;
188
188
  };
189
+ /**
190
+ * Default component classes
191
+ */
192
+ export const componentClasses = {
193
+ section: '',
194
+ container: 'relative z-10 transform rounded-xl mt-4',
195
+ icon_container: 'pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3',
196
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>',
197
+ input: 'border-1 w-full rounded-md border-0 shadow-sm bg-gray-100 px-4 py-2.5 pl-10 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 sm:text-sm',
198
+ kbd_container: 'absolute inset-y-0 right-0 flex py-1.5 pr-1.5',
199
+ kbd_escape: 'inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1',
200
+ kbd_up: 'inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6',
201
+ kbd_down: 'inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6',
202
+ ul: 'absolute z-50 -mb-2 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm divide-y divide-gray-100',
203
+ li: 'z-50 cursor-default select-none py-2 px-2 lg:px-4 text-gray-900 hover:bg-indigo-500 hover:text-white',
204
+ li_current: 'bg-indigo-500 text-white',
205
+ li_a: 'block w-full',
206
+ };
207
+ export const componentOptions = {
208
+ autofocus: false,
209
+ autocomplete: 'off',
210
+ classes: componentClasses,
211
+ placeholder: '',
212
+ show_distance: false
213
+ };
214
+ /**
215
+ * Validate and cast component options
216
+ * @param options
217
+ */
218
+ export const validateOptions = (options) => {
219
+ // If options is not an object, set it to an empty object
220
+ if (typeof options !== 'object' || Object.keys(options).length === 0) {
221
+ options = {
222
+ autofocus: false,
223
+ autocomplete: 'off',
224
+ classes: componentClasses,
225
+ placeholder: 'Start typing...',
226
+ show_distance: false
227
+ };
228
+ return options;
229
+ }
230
+ // Find the missing options properties
231
+ for (const key in componentOptions) {
232
+ if (!(key in options)) {
233
+ options[key] = componentOptions[key];
234
+ }
235
+ }
236
+ // Find the missing classes properties
237
+ for (const key in componentClasses) {
238
+ if (!(key in options.classes)) {
239
+ options.classes[key] = componentClasses[key];
240
+ }
241
+ }
242
+ return options;
243
+ };
@@ -23,22 +23,18 @@ export interface RequestParams {
23
23
  sessionToken?: string;
24
24
  }
25
25
  export interface ComponentClasses {
26
- section?: string;
27
- container?: string;
28
- icon_container?: string;
29
- icon?: string;
30
- input?: string;
31
- kbd_container?: string;
32
- kbd_escape?: string;
33
- kbd_up?: string;
34
- kbd_down?: string;
35
- ul?: string;
36
- li?: string;
37
- li_current?: string;
38
- li_a?: string;
26
+ [key: string]: string;
27
+ }
28
+ export interface ComponentOptions {
29
+ autofocus: boolean;
30
+ autocomplete: AutoFill;
31
+ classes: ComponentClasses;
32
+ placeholder: string;
33
+ show_distance: boolean;
39
34
  }
40
35
  export interface Props {
41
36
  PUBLIC_GOOGLE_MAPS_API_KEY: string;
37
+ options?: ComponentOptions;
42
38
  fetchFields?: string[];
43
39
  placeholder?: string;
44
40
  autofocus?: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "places-autocomplete-svelte",
3
3
  "license": "MIT",
4
- "version": "2.1.6",
4
+ "version": "2.1.8",
5
5
  "description": "A lightweight and customizable Svelte component for easy integration of Google Maps Places (New) Autocomplete in your Svelte/SvelteKit applications. Provides accessible autocomplete suggestions and detailed address retrieval.",
6
6
  "keywords": [
7
7
  "svelte",
@@ -67,29 +67,31 @@
67
67
  "svelte": "^5.1.4"
68
68
  },
69
69
  "devDependencies": {
70
- "@sveltejs/adapter-auto": "^3.3.1",
71
- "@sveltejs/adapter-cloudflare": "^5.0.0",
72
- "@sveltejs/kit": "^2.15.2",
73
- "@sveltejs/package": "^2.3.7",
70
+ "@sveltejs/adapter-auto": "^4.0.0",
71
+ "@sveltejs/adapter-cloudflare": "^5.0.1",
72
+ "@sveltejs/kit": "^2.16.1",
73
+ "@sveltejs/package": "^2.3.9",
74
74
  "@sveltejs/vite-plugin-svelte": "^5.0.3",
75
+ "@tailwindcss/postcss": "^4.0.3",
75
76
  "@tailwindcss/typography": "^0.5.16",
77
+ "@tailwindcss/vite": "^4.0.3",
76
78
  "@types/eslint": "^9.6.1",
77
79
  "autoprefixer": "^10.4.20",
78
- "eslint": "^9.17.0",
79
- "eslint-config-prettier": "^9.1.0",
80
+ "eslint": "^9.19.0",
81
+ "eslint-config-prettier": "^10.0.1",
80
82
  "eslint-plugin-svelte": "^2.46.1",
81
83
  "globals": "^15.14.0",
82
- "postcss": "^8.4.49",
84
+ "postcss": "^8.5.1",
83
85
  "prettier": "^3.4.2",
84
- "prettier-plugin-svelte": "^3.3.2",
85
- "publint": "^0.3.0",
86
- "svelte": "^5.17.1",
87
- "svelte-check": "^4.1.3",
88
- "tailwindcss": "^3.4.17",
86
+ "prettier-plugin-svelte": "^3.3.3",
87
+ "publint": "^0.3.2",
88
+ "svelte": "^5.19.6",
89
+ "svelte-check": "^4.1.4",
90
+ "tailwindcss": "^4.0.3",
89
91
  "tslib": "^2.8.1",
90
92
  "typescript": "^5.7.3",
91
- "typescript-eslint": "^8.19.1",
92
- "vite": "^6.0.7"
93
+ "typescript-eslint": "^8.22.0",
94
+ "vite": "^6.0.11"
93
95
  },
94
96
  "svelte": "./dist/index.js",
95
97
  "types": "./dist/PlaceAutocomplete.svelte.d.ts",