places-autocomplete-svelte 2.1.1 → 2.1.3

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,6 +1,7 @@
1
- # Places Autocomplete Svelte
1
+ # Places (New) Autocomplete Svelte
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.
2
4
 
3
- This Svelte component leverages the [Google Maps Places 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.
4
5
 
5
6
 
6
7
  ## Features:
@@ -9,9 +10,11 @@ This Svelte component leverages the [Google Maps Places Autocomplete API](https:
9
10
  - **Autocomplete Suggestions:** Provides real-time address suggestions as the user types.
10
11
  - **Detailed Address Retrieval:** Retrieve comprehensive address information, including street address, city, region, postal code, and country.
11
12
  - **Country/Region Filtering:** Refine search results by specifying countries or regions.
12
- - **Customizable:** Tailor the component's appearance (placeholder, language) and data retrieved (`fetchFields`).
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.
13
15
  - **Accessible:** Supports keyboard navigation for selecting suggestions.
14
16
 
17
+
15
18
  ## Demo
16
19
 
17
20
  See a live demo of the component in action: [Demo](https://places-autocomplete-demo.pages.dev/)
@@ -60,27 +63,18 @@ let onResponse = (response) => {
60
63
 
61
64
 
62
65
  ## Customization
63
-
64
- - `countries`: Use countries property to refine search by region
65
66
  - `placeholder`: Use the placeholder property to customize the input field's placeholder text.
66
67
  - `autocomplete`: Use to disable the HTML `<input>` autocomplete attribute.
67
68
  - `requestParams` (optional [AutocompleteRequest properties](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest) ):
68
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)
69
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`.
70
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.
71
73
 
72
74
  ```svelte
73
75
  <script>
74
76
  // ... other imports
75
77
 
76
- /**
77
- * @type array optional
78
- */
79
- let countries = [
80
- { name: 'United Kingdom', region: 'GB'},
81
- { name: 'United States', region: 'US' }
82
- // ... more countries
83
- ];
84
78
  /**
85
79
  * @type string optional
86
80
  */
@@ -106,6 +100,30 @@ const requestParams = {
106
100
  region : 'GB',
107
101
  }
108
102
 
103
+ /**
104
+ * @type object optional
105
+ * Component default Tailwind CSS classes
106
+ */
107
+ const classes = {
108
+ section: '',
109
+ container: 'relative z-10 transform rounded-xl mt-4',
110
+ icon_container: 'pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3',
111
+ 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>',
112
+ input:
113
+ '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',
114
+ kbd_container: 'absolute inset-y-0 right-0 flex py-1.5 pr-1.5',
115
+ kbd_escape:
116
+ 'inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1',
117
+ kbd_up:
118
+ 'inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6',
119
+ kbd_down:
120
+ 'inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6',
121
+ 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',
122
+ li: 'z-50 cursor-default select-none py-2 pl-4 text-gray-900 hover:bg-indigo-500 hover:text-white',
123
+ li_current: 'bg-indigo-500 text-white',
124
+ li_a: 'block w-full'
125
+ },
126
+
109
127
  /**
110
128
  * @type array optional
111
129
  */
@@ -116,10 +134,12 @@ const fetchFields = ['formattedAddress', 'addressComponents'];
116
134
  {onError}
117
135
  {onResponse}
118
136
  {PUBLIC_GOOGLE_MAPS_API_KEY}
119
- bind:countries
137
+ {requestParams}
120
138
  {placeholder}
121
139
  {autocompete}
122
- {fetchFields}/>
140
+ {fetchFields}
141
+ {classes}
142
+ />
123
143
 
124
144
  ```
125
145
 
@@ -130,11 +150,11 @@ const fetchFields = ['formattedAddress', 'addressComponents'];
130
150
  | `PUBLIC_GOOGLE_MAPS_API_KEY` | `String` | Your Google Maps Places API Key. | Yes | |
131
151
  | `onResponse` | `CustomEvent` | Dispatched when a place is selected, containing the place details. | Yes | |
132
152
  | `onError` | `CustomEvent` | Dispatched when an error occurs. | No | |
133
- | `countries` | `Array` | Array of countries/regions to filter results. | No | `[]` |
134
153
  | `placeholder` | `String` | Placeholder text for the input field. | No | `"Search..."` |
135
154
  | `autocomplete` | `string` | HTML `autocomplete` attribute for the input field. Set to "off" to disable browser autocomplete. | No | `"off"` |
136
155
  | `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 | `{}` |
137
156
  | `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']` |
157
+ | `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* |
138
158
 
139
159
 
140
160
  ## Error Handling
@@ -144,13 +164,13 @@ The `onError` event will be dispatched if there is an issue with the Google Maps
144
164
 
145
165
  ```svelte
146
166
  <script>
147
- // ... other imports
167
+ // ... other imports
148
168
 
149
- // Error handler
150
- let pacError = '';
151
- let onError = (error: string) => {
152
- console.error(error);
153
- pacError = error;
169
+ // Error handler
170
+ let pacError = '';
171
+ let onError = (error: string) => {
172
+ console.error(error);
173
+ pacError = error;
154
174
  };
155
175
  </script>
156
176
 
@@ -170,6 +190,4 @@ Contributions are welcome! Please open an issue or submit a pull request on the
170
190
 
171
191
  ## License
172
192
 
173
- [MIT](LICENSE)
174
-
175
-
193
+ [MIT](LICENSE)
@@ -2,10 +2,7 @@
2
2
  import { onMount } from 'svelte';
3
3
  import * as GMaps from '@googlemaps/js-api-loader';
4
4
  import type { Props } from './interfaces.js';
5
- import {
6
- validateRequestParams,
7
- requestParamsDefault,
8
- } from './helpers.js';
5
+ import { validateRequestParams } from './helpers.js';
9
6
  const { Loader } = GMaps;
10
7
 
11
8
  let {
@@ -15,28 +12,55 @@
15
12
  */
16
13
  PUBLIC_GOOGLE_MAPS_API_KEY,
17
14
  fetchFields = $bindable(['formattedAddress', 'addressComponents']),
18
- countries = $bindable([]),
19
15
  placeholder = 'Search...',
20
16
  autocompete = 'off',
17
+ classes = {
18
+ section: '',
19
+ container: 'relative z-10 transform rounded-xl mt-4',
20
+ icon_container: 'pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3',
21
+ 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>',
22
+ input:
23
+ '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',
24
+ kbd_container: 'absolute inset-y-0 right-0 flex py-1.5 pr-1.5',
25
+ kbd_escape:
26
+ 'inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1',
27
+ kbd_up:
28
+ 'inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6',
29
+ kbd_down:
30
+ 'inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6',
31
+ 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',
32
+ li: 'z-50 cursor-default select-none py-2 pl-4 text-gray-900 hover:bg-indigo-500 hover:text-white',
33
+ li_current: 'bg-indigo-500 text-white',
34
+ li_a: 'block w-full'
35
+ },
21
36
  onResponse = $bindable((e: Event) => {}),
22
37
  onError = $bindable((error: string) => {}),
23
38
  requestParams
24
39
  }: Props = $props();
25
40
 
26
- // Check if countries are available
27
- let hasCountries = countries.length > 0;
41
+ // set classes as state
42
+ let cl = $state(classes);
43
+
44
+ // reset keyboard classes
45
+ const resetKbdClasses = () => {
46
+ cl.kbd_down = classes.kbd_down;
47
+ cl.kbd_up = classes.kbd_up;
48
+ }
49
+
50
+
28
51
  // Local variables
29
52
  let inputRef: HTMLInputElement;
30
53
  let currentSuggestion = $state(-1);
31
- let title: string = $state('');
32
54
  let results: any[] = $state([]);
33
55
  let loader: GMaps.Loader;
34
56
  let placesApi: { [key: string]: any } = {};
35
57
  //https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data
36
58
  // validate and merge requestParams with requestParamsDefault
37
- let request = $state(validateRequestParams(Object.assign(requestParamsDefault, requestParams)));
38
-
59
+ //let request = $state(validateRequestParams(Object.assign(requestParamsDefault, requestParams)));
60
+ requestParams = validateRequestParams(requestParams);
61
+ let request = $state(requestParams);
39
62
 
63
+ // clear result when input is empty
40
64
  $effect(() => {
41
65
  if (request.input == '') {
42
66
  results = [];
@@ -62,7 +86,7 @@
62
86
  ): Promise<void> => {
63
87
  const target = event.currentTarget as HTMLInputElement;
64
88
  if (target?.value == '') {
65
- title = '';
89
+ //title = '';
66
90
  request.input = '';
67
91
  results = [];
68
92
  return;
@@ -130,7 +154,7 @@
130
154
  request.sessionToken = new placesApi.AutocompleteSessionToken();
131
155
  } catch (e: any) {
132
156
  onError((e.name || 'An error occurred') + ' - ' + (e.message || 'error fetch token'));
133
- }
157
+ }
134
158
  };
135
159
 
136
160
  /**
@@ -165,8 +189,12 @@
165
189
  function onKeyDown(e: KeyboardEvent) {
166
190
  if (e.key === 'ArrowDown') {
167
191
  currentSuggestion = Math.min(currentSuggestion + 1, results.length - 1);
192
+ resetKbdClasses();
193
+ cl.kbd_down += ' bg-indigo-500 text-white';
168
194
  } else if (e.key === 'ArrowUp') {
169
195
  currentSuggestion = Math.max(currentSuggestion - 1, 0);
196
+ resetKbdClasses();
197
+ cl.kbd_up += ' bg-indigo-500 text-white';
170
198
  } else if (e.key === 'Enter') {
171
199
  e.preventDefault();
172
200
  if (currentSuggestion >= 0) {
@@ -176,655 +204,76 @@
176
204
  // reset srarch input and results
177
205
  reset();
178
206
  }
207
+
208
+ setTimeout(() => {
209
+ resetKbdClasses();
210
+ }, 300);
179
211
  }
180
212
  </script>
181
213
 
182
- <svelte:window on:keydown={onKeyDown} />
183
-
184
- <section class="my-10">
185
- <div class="grid grid-cols-1 lg:grid-cols-6 gap-x-4">
186
- <div class:lg:col-span-4={hasCountries} class:lg:col-span-6={!hasCountries}>
187
- <label class="mt-1 text-sm leading-6 text-gray-600" for="search"
188
- >Start typing your address</label
189
- >
190
- <div class="relative z-10 transform rounded-xl mt-4">
191
- <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
192
- <svg
193
- xmlns="http://www.w3.org/2000/svg"
194
- class="w-5 h-5"
195
- viewBox="0 0 24 24"
196
- fill="none"
197
- stroke="currentColor"
198
- stroke-width="2"
199
- stroke-linecap="round"
200
- stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg
201
- >
202
- </div>
203
-
204
- <input
205
- type="text"
206
- name="search"
207
- bind:this={inputRef}
208
- class="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"
209
- {placeholder}
210
- autocomplete={autocompete}
211
- aria-controls="options"
212
- bind:value={request.input}
213
- oninput={makeAcRequest}
214
- />
214
+ <svelte:window onkeydown={onKeyDown} />
215
215
 
216
- {#if results.length > 0}
217
- <div class="absolute inset-y-0 right-0 flex py-1.5 pr-1.5">
218
- <kbd
219
- class="inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1"
220
- >Esc</kbd
221
- >
222
- <kbd
223
- class="inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6"
224
- >&uArr;</kbd
225
- >
226
- <kbd
227
- class="inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6"
228
- >&dArr;</kbd
229
- >
230
- </div>
231
- <ul
232
- class="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"
233
- id="options"
234
- >
235
- {#each results as place, i}
236
- <li
237
- class="z-50 cursor-default select-none py-2 pl-4 text-gray-900 hover:bg-indigo-500 hover:text-white"
238
- class:bg-indigo-500={i === currentSuggestion}
239
- class:bg-white={i !== currentSuggestion}
240
- class:text-white={i === currentSuggestion}
241
- id="option-{i + 1}"
242
- >
243
- <!-- svelte-ignore a11y_invalid_attribute -->
244
- <a
245
- href="javascript:void(0)"
246
- class="block w-full"
247
- tabindex={i + 1}
248
- onclick={() => onPlaceSelected(place.to_pace)}
249
- >
250
- {place.text}
251
- </a>
252
- </li>
253
- {/each}
254
- </ul>
255
- {/if}
256
- </div>
216
+ <section class="{classes?.section}">
217
+ <div class="{classes.container}">
218
+ {#if classes.icon}
219
+ <div class="{classes.icon_container}">
220
+ {@html classes.icon}
257
221
  </div>
222
+ {/if}
258
223
 
259
- <div class:lg:col-span-2={hasCountries} class:hidden={!hasCountries}>
260
- <label class="mt-1 text-sm leading-6 text-gray-600" for="search">Address country</label>
261
- <div class="flex items-center mt-4">
262
- <label for="country" class="sr-only">Country</label>
263
- <select
264
- id="country"
265
- name="country"
266
- class="h-10 w-full rounded-md border-0 bg-transparent py-0 pl-2 pr-7 text-gray-500 focus:ring-2 ring-1 ring-inset ring-gray-300 focus:ring-inset focus:ring-indigo-600 sm:text-sm"
267
- bind:value={request.region}
268
- >
269
- {#each countries as country}
270
- <option value={country.region}>{country.name}</option>
271
- {/each}
272
- </select>
273
- </div>
224
+ <input
225
+ type="text"
226
+ name="search"
227
+ bind:this={inputRef}
228
+ class="{classes.input}"
229
+ {placeholder}
230
+ autocomplete={autocompete}
231
+ aria-controls="options"
232
+ aria-autocomplete="list"
233
+ aria-owns="options"
234
+ aria-labelledby="search"
235
+ aria-label="Search"
236
+ aria-haspopup="listbox"
237
+ bind:value={request.input}
238
+ oninput={makeAcRequest}
239
+ />
240
+
241
+ {#if results.length > 0}
242
+ <div class="{classes.kbd_container}">
243
+ <kbd
244
+ class="{classes.kbd_escape}"
245
+ >Esc</kbd
246
+ >
247
+ <kbd
248
+ class="{cl.kbd_up}"
249
+ >&uArr;</kbd
250
+ >
251
+ <kbd
252
+ class="{cl.kbd_down}"
253
+ >&dArr;</kbd
254
+ >
274
255
  </div>
256
+ <ul
257
+ class="{classes.ul}"
258
+ id="options"
259
+ >
260
+ {#each results as place, i}
261
+ <li
262
+ class={[ classes.li, i === currentSuggestion && classes.li_current ]}
263
+ id="option-{i + 1}"
264
+ >
265
+ <!-- svelte-ignore a11y_invalid_attribute -->
266
+ <a
267
+ href="javascript:void(0)"
268
+ class="{classes?.li_a}"
269
+ tabindex={i + 1}
270
+ onclick={() => onPlaceSelected(place.to_pace)}
271
+ >
272
+ {place.text}
273
+ </a>
274
+ </li>
275
+ {/each}
276
+ </ul>
277
+ {/if}
275
278
  </div>
276
279
  </section>
277
-
278
- <style>
279
- input,
280
- select,
281
- ul {
282
- margin: 0;
283
- padding: 0;
284
- }
285
- .absolute,
286
- .sr-only {
287
- position: absolute;
288
- }
289
- .block,
290
- svg {
291
- display: block;
292
- }
293
- *,
294
- .border-gray-300 {
295
- --tw-border-opacity: 1;
296
- }
297
- .text-gray-500,
298
- .text-gray-900,
299
- .text-white {
300
- --tw-text-opacity: 1;
301
- }
302
- *,
303
- :after,
304
- :before {
305
- box-sizing: border-box;
306
- border: 0 solid #e5e7eb;
307
- --tw-border-spacing-x: 0;
308
- --tw-border-spacing-y: 0;
309
- --tw-translate-x: 0;
310
- --tw-translate-y: 0;
311
- --tw-rotate: 0;
312
- --tw-skew-x: 0;
313
- --tw-skew-y: 0;
314
- --tw-scale-x: 1;
315
- --tw-scale-y: 1;
316
- --tw-pan-x: ;
317
- --tw-pan-y: ;
318
- --tw-pinch-zoom: ;
319
- --tw-scroll-snap-strictness: proximity;
320
- --tw-gradient-from-position: ;
321
- --tw-gradient-via-position: ;
322
- --tw-gradient-to-position: ;
323
- --tw-ordinal: ;
324
- --tw-slashed-zero: ;
325
- --tw-numeric-figure: ;
326
- --tw-numeric-spacing: ;
327
- --tw-numeric-fraction: ;
328
- --tw-ring-inset: ;
329
- --tw-ring-offset-width: 0px;
330
- --tw-ring-offset-color: #fff;
331
- --tw-ring-color: rgb(59 130 246 / 0.5);
332
- --tw-ring-offset-shadow: 0 0 #0000;
333
- --tw-ring-shadow: 0 0 #0000;
334
- --tw-shadow: 0 0 #0000;
335
- --tw-shadow-colored: 0 0 #0000;
336
- --tw-blur: ;
337
- --tw-brightness: ;
338
- --tw-contrast: ;
339
- --tw-grayscale: ;
340
- --tw-hue-rotate: ;
341
- --tw-invert: ;
342
- --tw-saturate: ;
343
- --tw-sepia: ;
344
- --tw-drop-shadow: ;
345
- --tw-backdrop-blur: ;
346
- --tw-backdrop-brightness: ;
347
- --tw-backdrop-contrast: ;
348
- --tw-backdrop-grayscale: ;
349
- --tw-backdrop-hue-rotate: ;
350
- --tw-backdrop-invert: ;
351
- --tw-backdrop-opacity: ;
352
- --tw-backdrop-saturate: ;
353
- --tw-backdrop-sepia: ;
354
- --tw-contain-size: ;
355
- --tw-contain-layout: ;
356
- --tw-contain-paint: ;
357
- --tw-contain-style: ;
358
- }
359
- :after,
360
- :before {
361
- --tw-content: '';
362
- }
363
- :host,
364
- section {
365
- line-height: 1.5;
366
- -webkit-text-size-adjust: 100%;
367
- -moz-tab-size: 4;
368
- -o-tab-size: 4;
369
- tab-size: 4;
370
- font-family:
371
- system-ui,
372
- sans-serif,
373
- apple color emoji,
374
- segoe ui emoji,
375
- Segoe UI Symbol,
376
- noto color emoji;
377
- font-feature-settings: normal;
378
- font-variation-settings: normal;
379
- -webkit-tap-highlight-color: transparent;
380
- }
381
- kbd {
382
- font-family:
383
- ui-monospace,
384
- SFMono-Regular,
385
- Menlo,
386
- Monaco,
387
- Consolas,
388
- Liberation Mono,
389
- Courier New,
390
- monospace;
391
- font-feature-settings: normal;
392
- font-variation-settings: normal;
393
- font-size: 1em;
394
- }
395
- input,
396
- select {
397
- font-family: inherit;
398
- font-feature-settings: inherit;
399
- font-variation-settings: inherit;
400
- font-size: 100%;
401
- font-weight: inherit;
402
- line-height: inherit;
403
- letter-spacing: inherit;
404
- color: inherit;
405
- }
406
- :-moz-focusring {
407
- outline: auto;
408
- }
409
- :-moz-ui-invalid {
410
- box-shadow: none;
411
- }
412
- ::-webkit-inner-spin-button,
413
- ::-webkit-outer-spin-button {
414
- height: auto;
415
- }
416
- ::-webkit-search-decoration {
417
- -webkit-appearance: none;
418
- }
419
- ::-webkit-file-upload-button {
420
- -webkit-appearance: button;
421
- font: inherit;
422
- }
423
- ul {
424
- list-style: none;
425
- }
426
- .cursor-default,
427
- :disabled {
428
- cursor: default;
429
- }
430
- svg {
431
- vertical-align: middle;
432
- }
433
- [type='text'],
434
- input:where(:not([type])),
435
- select {
436
- -webkit-appearance: none;
437
- -moz-appearance: none;
438
- appearance: none;
439
- background-color: #fff;
440
- border-color: #6b7280;
441
- border-width: 1px;
442
- border-radius: 0;
443
- padding: 0.5rem 0.75rem;
444
- font-size: 1rem;
445
- line-height: 1.5rem;
446
- --tw-shadow: 0 0 #0000;
447
- }
448
- [type='text']:focus,
449
- input:where(:not([type])):focus,
450
- select:focus {
451
- outline: transparent solid 2px;
452
- outline-offset: 2px;
453
- --tw-ring-inset: var(--tw-empty,);
454
- --tw-ring-offset-width: 0px;
455
- --tw-ring-offset-color: #fff;
456
- --tw-ring-color: #2563eb;
457
- --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
458
- var(--tw-ring-offset-color);
459
- --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width))
460
- var(--tw-ring-color);
461
- box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
462
- border-color: #2563eb;
463
- }
464
- input::-moz-placeholder {
465
- color: #6b7280;
466
- opacity: 1;
467
- }
468
- input::placeholder {
469
- color: #6b7280;
470
- opacity: 1;
471
- }
472
- select {
473
- text-transform: none;
474
- background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAyMCAyMCc+PHBhdGggc3Ryb2tlPScjNmI3MjgwJyBzdHJva2UtbGluZWNhcD0ncm91bmQnIHN0cm9rZS1saW5lam9pbj0ncm91bmQnIHN0cm9rZS13aWR0aD0nMS41JyBkPSdNNiA4bDQgNCA0LTQnLz48L3N2Zz4=);
475
- background-position: right 0.5rem center;
476
- background-repeat: no-repeat;
477
- background-size: 1.5em 1.5em;
478
- padding-right: 2.5rem;
479
- -webkit-print-color-adjust: exact;
480
- print-color-adjust: exact;
481
- }
482
- :root {
483
- --background: 0 0% 100%;
484
- --foreground: 222.2 84% 4.9%;
485
- --muted: 210 40% 96.1%;
486
- --muted-foreground: 215.4 16.3% 46.9%;
487
- --popover: 0 0% 100%;
488
- --popover-foreground: 222.2 84% 4.9%;
489
- --card: 0 0% 100%;
490
- --card-foreground: 222.2 84% 4.9%;
491
- --border: 214.3 31.8% 91.4%;
492
- --input: 214.3 31.8% 91.4%;
493
- --primary: 222.2 47.4% 11.2%;
494
- --primary-foreground: 210 40% 98%;
495
- --secondary: 210 40% 96.1%;
496
- --secondary-foreground: 222.2 47.4% 11.2%;
497
- --accent: 210 40% 96.1%;
498
- --accent-foreground: 222.2 47.4% 11.2%;
499
- --destructive: 0 72.2% 50.6%;
500
- --destructive-foreground: 210 40% 98%;
501
- --ring: 222.2 84% 4.9%;
502
- --radius: 0.5rem;
503
- }
504
- * {
505
- border-color: hsl(var(--border) / var(--tw-border-opacity));
506
- }
507
- ::backdrop {
508
- --tw-border-spacing-x: 0;
509
- --tw-border-spacing-y: 0;
510
- --tw-translate-x: 0;
511
- --tw-translate-y: 0;
512
- --tw-rotate: 0;
513
- --tw-skew-x: 0;
514
- --tw-skew-y: 0;
515
- --tw-scale-x: 1;
516
- --tw-scale-y: 1;
517
- --tw-pan-x: ;
518
- --tw-pan-y: ;
519
- --tw-pinch-zoom: ;
520
- --tw-scroll-snap-strictness: proximity;
521
- --tw-gradient-from-position: ;
522
- --tw-gradient-via-position: ;
523
- --tw-gradient-to-position: ;
524
- --tw-ordinal: ;
525
- --tw-slashed-zero: ;
526
- --tw-numeric-figure: ;
527
- --tw-numeric-spacing: ;
528
- --tw-numeric-fraction: ;
529
- --tw-ring-inset: ;
530
- --tw-ring-offset-width: 0px;
531
- --tw-ring-offset-color: #fff;
532
- --tw-ring-color: rgb(59 130 246 / 0.5);
533
- --tw-ring-offset-shadow: 0 0 #0000;
534
- --tw-ring-shadow: 0 0 #0000;
535
- --tw-shadow: 0 0 #0000;
536
- --tw-shadow-colored: 0 0 #0000;
537
- --tw-blur: ;
538
- --tw-brightness: ;
539
- --tw-contrast: ;
540
- --tw-grayscale: ;
541
- --tw-hue-rotate: ;
542
- --tw-invert: ;
543
- --tw-saturate: ;
544
- --tw-sepia: ;
545
- --tw-drop-shadow: ;
546
- --tw-backdrop-blur: ;
547
- --tw-backdrop-brightness: ;
548
- --tw-backdrop-contrast: ;
549
- --tw-backdrop-grayscale: ;
550
- --tw-backdrop-hue-rotate: ;
551
- --tw-backdrop-invert: ;
552
- --tw-backdrop-opacity: ;
553
- --tw-backdrop-saturate: ;
554
- --tw-backdrop-sepia: ;
555
- --tw-contain-size: ;
556
- --tw-contain-layout: ;
557
- --tw-contain-paint: ;
558
- --tw-contain-style: ;
559
- }
560
- .sr-only {
561
- width: 1px;
562
- height: 1px;
563
- padding: 0;
564
- margin: -1px;
565
- overflow: hidden;
566
- clip: rect(0, 0, 0, 0);
567
- white-space: nowrap;
568
- border-width: 0;
569
- }
570
- .pointer-events-none {
571
- pointer-events: none;
572
- }
573
- .relative {
574
- position: relative;
575
- }
576
- .inset-y-0 {
577
- top: 0;
578
- bottom: 0;
579
- }
580
- .left-0 {
581
- left: 0;
582
- }
583
- .right-0 {
584
- right: 0;
585
- }
586
- .z-50 {
587
- z-index: 50;
588
- }
589
- .-mb-2 {
590
- margin-bottom: -0.5rem;
591
- }
592
- .mr-1 {
593
- margin-right: 0.25rem;
594
- }
595
- .mt-4 {
596
- margin-top: 1rem;
597
- }
598
- .flex {
599
- display: flex;
600
- }
601
- .inline-flex {
602
- display: inline-flex;
603
- }
604
- .hidden {
605
- display: none;
606
- }
607
- .h-10 {
608
- height: 2.5rem;
609
- }
610
- .h-5 {
611
- height: 1.25rem;
612
- }
613
- .max-h-60 {
614
- max-height: 15rem;
615
- }
616
- .w-5 {
617
- width: 1.25rem;
618
- }
619
- .w-6 {
620
- width: 1.5rem;
621
- }
622
- .w-8 {
623
- width: 2rem;
624
- }
625
- .w-full {
626
- width: 100%;
627
- }
628
- .transform {
629
- transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate))
630
- skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x))
631
- scaleY(var(--tw-scale-y));
632
- }
633
- .select-none {
634
- -webkit-user-select: none;
635
- -moz-user-select: none;
636
- user-select: none;
637
- }
638
- .grid-cols-1 {
639
- grid-template-columns: repeat(1, minmax(0, 1fr));
640
- grid-template-columns: 1fr;
641
- }
642
- .items-center {
643
- align-items: center;
644
- }
645
- .gap-x-4 {
646
- -moz-column-gap: 1rem;
647
- column-gap: 1rem;
648
- row-gap: 1rem;
649
- }
650
- .rounded-md {
651
- border-radius: calc(var(--radius) - 2px);
652
- }
653
- .rounded-xl {
654
- border-radius: 0.75rem;
655
- }
656
- .border {
657
- border-width: 1px;
658
- }
659
- .border-gray-300 {
660
- border-color: rgb(209 213 219 / var(--tw-border-opacity));
661
- }
662
- .bg-gray-100 {
663
- --tw-bg-opacity: 1;
664
- background-color: rgb(243 244 246 / var(--tw-bg-opacity));
665
- }
666
- .bg-indigo-500,
667
- .hover\:bg-indigo-500:hover {
668
- --tw-bg-opacity: 1;
669
- background-color: rgb(99 102 241 / var(--tw-bg-opacity));
670
- }
671
- .bg-transparent {
672
- background-color: transparent;
673
- }
674
- .bg-white {
675
- --tw-bg-opacity: 1;
676
- background-color: rgb(255 255 255 / var(--tw-bg-opacity));
677
- }
678
- .px-1 {
679
- padding-left: 0.25rem;
680
- padding-right: 0.25rem;
681
- }
682
- .px-4 {
683
- padding-left: 1rem;
684
- padding-right: 1rem;
685
- }
686
- .py-0 {
687
- padding-top: 0;
688
- padding-bottom: 0;
689
- }
690
- .py-1 {
691
- padding-top: 0.25rem;
692
- padding-bottom: 0.25rem;
693
- }
694
- .py-1\.5 {
695
- padding-top: 0.375rem;
696
- padding-bottom: 0.375rem;
697
- }
698
- .py-2 {
699
- padding-top: 0.5rem;
700
- padding-bottom: 0.5rem;
701
- }
702
- .py-2\.5 {
703
- padding-top: 0.625rem;
704
- padding-bottom: 0.625rem;
705
- }
706
- .pl-10 {
707
- padding-left: 2.5rem;
708
- }
709
- .pl-2 {
710
- padding-left: 0.5rem;
711
- }
712
- .pl-3 {
713
- padding-left: 0.75rem;
714
- }
715
- .pl-4 {
716
- padding-left: 1rem;
717
- }
718
- .pr-1\.5 {
719
- padding-right: 0.375rem;
720
- }
721
- .pr-20 {
722
- padding-right: 5rem;
723
- }
724
- .pr-7 {
725
- padding-right: 1.75rem;
726
- }
727
- .text-base {
728
- font-size: 1rem;
729
- line-height: 1.5rem;
730
- }
731
- .text-sm {
732
- font-size: 0.875rem;
733
- line-height: 1.25rem;
734
- }
735
- .text-xs {
736
- font-size: 0.75rem;
737
- line-height: 1rem;
738
- }
739
- .text-gray-500 {
740
- color: rgb(107 114 128 / var(--tw-text-opacity));
741
- }
742
- .text-gray-900 {
743
- color: rgb(17 24 39 / var(--tw-text-opacity));
744
- }
745
- .text-white {
746
- color: rgb(255 255 255 / var(--tw-text-opacity));
747
- }
748
- .shadow-lg {
749
- --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
750
- --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color),
751
- 0 4px 6px -4px var(--tw-shadow-color);
752
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
753
- var(--tw-shadow);
754
- }
755
- .focus\:ring-2:focus,
756
- .ring-1 {
757
- --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
758
- var(--tw-ring-offset-color);
759
- box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
760
- }
761
- .ring-1 {
762
- --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width))
763
- var(--tw-ring-color);
764
- }
765
- .ring-black {
766
- --tw-ring-opacity: 1;
767
- --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity));
768
- }
769
- .focus\:outline-none:focus {
770
- outline: transparent solid 2px;
771
- outline-offset: 2px;
772
- }
773
- .focus\:ring-2:focus {
774
- --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width))
775
- var(--tw-ring-color);
776
- }
777
- .focus\:ring-inset:focus {
778
- --tw-ring-inset: inset;
779
- }
780
- @media (min-width: 640px) {
781
- .sm\:text-sm {
782
- font-size: 0.875rem;
783
- line-height: 1.25rem;
784
- }
785
- }
786
- @media (min-width: 1024px) {
787
- .lg\:col-span-2 {
788
- grid-column: span 2 / span 2;
789
- }
790
- }
791
- @keyframes swipe-out {
792
- 0% {
793
- transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));
794
- opacity: 1;
795
- }
796
- to {
797
- transform: translateY(
798
- calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%)
799
- );
800
- opacity: 0;
801
- }
802
- }
803
- .grid {
804
- display: grid;
805
- }
806
- @media (min-width: 768px) {
807
- .lg\:grid-cols-6 {
808
- grid-template-columns: repeat(6, 1fr);
809
- }
810
- .lg\:col-span-6 {
811
- grid-column-start: span 6;
812
- }
813
- .lg\:col-span-4 {
814
- grid-column-start: span 4;
815
- }
816
- .lg\:col-span-2 {
817
- grid-column-start: span 2;
818
- }
819
- }
820
- .my-10 {
821
- margin-top: 2.5rem;
822
- margin-bottom: 2.5rem;
823
- }
824
- .justify-center {
825
- justify-content: center;
826
- }
827
- .hover\:text-white:hover {
828
- color: rgb(255 255 255 / var(--tw-text-opacity));
829
- }
830
- </style>
@@ -1,4 +1,4 @@
1
1
  import type { Props } from './interfaces.js';
2
- declare const PlaceAutocomplete: import("svelte").Component<Props, {}, "fetchFields" | "countries" | "onResponse" | "onError">;
2
+ declare const PlaceAutocomplete: import("svelte").Component<Props, {}, "fetchFields" | "onResponse" | "onError">;
3
3
  type PlaceAutocomplete = ReturnType<typeof PlaceAutocomplete>;
4
4
  export default PlaceAutocomplete;
package/dist/helpers.js CHANGED
@@ -114,6 +114,8 @@ export const validateRequestParams = (requestParams) => {
114
114
  delete requestParams[key];
115
115
  }
116
116
  }
117
+ // merge requestParams with requestParamsDefault
118
+ requestParams = Object.assign(requestParamsDefault, requestParams);
117
119
  // Reset sessionToken to empty string if passed to the component
118
120
  if (requestParams.sessionToken) {
119
121
  requestParams.sessionToken = String('');
@@ -22,6 +22,21 @@ export interface RequestParams {
22
22
  region?: string;
23
23
  sessionToken?: string;
24
24
  }
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;
39
+ }
25
40
  export interface Props {
26
41
  PUBLIC_GOOGLE_MAPS_API_KEY: string;
27
42
  fetchFields?: string[];
@@ -30,6 +45,7 @@ export interface Props {
30
45
  region: string;
31
46
  }[];
32
47
  placeholder?: string;
48
+ classes?: ComponentClasses;
33
49
  autocompete?: AutoFill;
34
50
  requestParams: RequestParams;
35
51
  onResponse: (e: Event) => void;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "places-autocomplete-svelte",
3
3
  "license": "MIT",
4
- "version": "2.1.1",
5
- "description": "A lightweight and customizable Svelte component for easy integration of Google Maps Places Autocomplete (New API) in your Svelte/SvelteKit applications. Provides accessible autocomplete suggestions and detailed address retrieval.",
4
+ "version": "2.1.3",
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",
8
8
  "sveltekit",
@@ -68,28 +68,28 @@
68
68
  },
69
69
  "devDependencies": {
70
70
  "@sveltejs/adapter-auto": "^3.3.1",
71
- "@sveltejs/adapter-cloudflare": "^4.7.4",
72
- "@sveltejs/kit": "^2.8.2",
71
+ "@sveltejs/adapter-cloudflare": "^5.0.0",
72
+ "@sveltejs/kit": "^2.15.1",
73
73
  "@sveltejs/package": "^2.3.7",
74
- "@sveltejs/vite-plugin-svelte": "^4.0.1",
74
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
75
75
  "@tailwindcss/typography": "^0.5.15",
76
76
  "@types/eslint": "^9.6.1",
77
77
  "autoprefixer": "^10.4.20",
78
- "eslint": "^9.15.0",
78
+ "eslint": "^9.17.0",
79
79
  "eslint-config-prettier": "^9.1.0",
80
- "eslint-plugin-svelte": "^2.46.0",
81
- "globals": "^15.12.0",
80
+ "eslint-plugin-svelte": "^2.46.1",
81
+ "globals": "^15.14.0",
82
82
  "postcss": "^8.4.49",
83
- "prettier": "^3.3.3",
83
+ "prettier": "^3.4.2",
84
84
  "prettier-plugin-svelte": "^3.3.2",
85
85
  "publint": "^0.2.12",
86
- "svelte": "^5.2.7",
87
- "svelte-check": "^4.1.0",
88
- "tailwindcss": "^3.4.15",
86
+ "svelte": "^5.16.0",
87
+ "svelte-check": "^4.1.1",
88
+ "tailwindcss": "^3.4.17",
89
89
  "tslib": "^2.8.1",
90
90
  "typescript": "^5.7.2",
91
- "typescript-eslint": "^8.15.0",
92
- "vite": "^5.4.11"
91
+ "typescript-eslint": "^8.19.0",
92
+ "vite": "^6.0.6"
93
93
  },
94
94
  "svelte": "./dist/index.js",
95
95
  "types": "./dist/PlaceAutocomplete.svelte.d.ts",