places-autocomplete-svelte 2.2.15 → 2.2.17
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 +80 -87
- package/dist/PlaceAutocomplete.svelte +33 -20
- package/dist/gmaps.d.ts +34 -0
- package/dist/gmaps.js +87 -0
- package/dist/interfaces.d.ts +7 -1
- package/package.json +26 -21
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/places-autocomplete-svelte)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
A flexible, accessible, and secure [Svelte](https://kit.svelte.dev) component leveraging the [Google Maps Places Autocomplete API (New)](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview)
|
|
6
|
+
A flexible, accessible, and secure [Svelte](https://kit.svelte.dev) component leveraging the [Google Maps Places Autocomplete API (New)](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview).
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The component handles API loading, session tokens, debounced fetching, and accessibility, allowing you to focus on building your application. It intelligently manages the Google Maps API loader, creating a shared instance that prevents conflicts with other map components on the same page.
|
|
9
9
|
|
|
10
10
|
## Available: Standalone JavaScript Library
|
|
11
11
|
|
|
@@ -15,16 +15,16 @@ Need this functionality for a non-Svelte project? Check out our companion vanill
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
17
|
* Integrates with the modern **Google Maps Places Autocomplete API (New)**.
|
|
18
|
+
* **Automatic Shared Loader:** Intelligently creates a single Google Maps loader instance and shares it via Svelte's context.
|
|
18
19
|
* **Highly Accessible:** Follows WAI-ARIA patterns for comboboxes, with full keyboard navigation and screen reader support.
|
|
19
20
|
* **Secure:** Safely renders suggestions to protect against XSS attacks.
|
|
20
|
-
* Automatically handles **session tokens** for cost management
|
|
21
|
+
* Automatically handles **session tokens** for cost management.
|
|
21
22
|
* **Debounced Input:** Limits API calls while the user is typing (configurable).
|
|
22
23
|
* **Suggestion Highlighting:** Automatically highlights the portion of text matching the user's input.
|
|
23
24
|
* **Imperative API:** Exposes `clear()`, `focus()`, and `getRequestParams()` methods for direct control.
|
|
24
25
|
* **Customisable Styling:** Easily override default styles using the `options.classes` prop.
|
|
25
26
|
* **TypeScript Support:** Fully written in TypeScript with included type definitions.
|
|
26
27
|
* **Event Handling:** Provides `onResponse` and `onError` callbacks.
|
|
27
|
-
* **Configurable:** Control API parameters (`requestParams`), requested data fields (`fetchFields`), and component behavior (`options`).
|
|
28
28
|
|
|
29
29
|
## Demo
|
|
30
30
|
|
|
@@ -51,6 +51,76 @@ npm install places-autocomplete-svelte
|
|
|
51
51
|
yarn add places-autocomplete-svelte
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
The `PlaceAutocomplete` component is designed for simplicity. Here’s the minimum required to get it working:
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// In your +page.svelte or a parent component
|
|
61
|
+
|
|
62
|
+
<script lang="ts">
|
|
63
|
+
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
|
|
64
|
+
import type { PlaceResult } from 'places-autocomplete-svelte/interfaces';
|
|
65
|
+
|
|
66
|
+
// Get API Key securely (e.g., from environment variables)
|
|
67
|
+
const PUBLIC_GOOGLE_MAPS_API_KEY = import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY;
|
|
68
|
+
|
|
69
|
+
// --- Handle Component Response ---
|
|
70
|
+
const handleResponse = (response: PlaceResult) => {
|
|
71
|
+
console.log('Place Selected:', response.formattedAddress);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// --- Handle Component Errors ---
|
|
75
|
+
const handleError = (error: string) => {
|
|
76
|
+
console.error('Error:', error);
|
|
77
|
+
};
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<!-- Add the component to your page -->
|
|
81
|
+
<PlaceAutocomplete {onResponse} {onError} {PUBLIC_GOOGLE_MAPS_API_KEY} />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Advanced: Using with other Google Maps Libraries
|
|
85
|
+
|
|
86
|
+
The `PlaceAutocomplete` component intelligently manages the Google Maps loader. For simple use cases, it handles loading automatically. However, if you need to use other Google Maps libraries (like `maps` or `marker`) on the same page, you should initialise the loader once in a parent component (`+page.svelte` or `+layout.svelte`). This prevents conflicts and ensures all libraries are loaded efficiently.
|
|
87
|
+
|
|
88
|
+
The component provides helper functions (`setGMapsContext`, `getGMapsContext`, `initialiseGMaps`) to manage this.
|
|
89
|
+
|
|
90
|
+
Here is how you would set it up in your `+page.svelte`:
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// src/routes/+page.svelte
|
|
94
|
+
<script lang="ts">
|
|
95
|
+
import { onMount } from 'svelte';
|
|
96
|
+
import { browser } from '$app/environment';
|
|
97
|
+
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
|
|
98
|
+
import { initialiseGMaps, setGMapsContext, getGMapsContext } from 'places-autocomplete-svelte/gmaps';
|
|
99
|
+
|
|
100
|
+
// 1. Set the context for Google Maps. This should be done in the script's top-level scope.
|
|
101
|
+
setGMapsContext();
|
|
102
|
+
|
|
103
|
+
// 2. If we are in the browser, trigger the asynchronous loading process.
|
|
104
|
+
if (browser) {
|
|
105
|
+
initialiseGMaps({
|
|
106
|
+
key: import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY,
|
|
107
|
+
v: 'weekly'
|
|
108
|
+
}).catch((error: any) => {
|
|
109
|
+
// ... handle error (already logged in initialiseGMaps)
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ... rest of your component logic
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<!-- The PlaceAutocomplete component will automatically use the context -->
|
|
117
|
+
<!-- You do NOT need to pass the `PUBLIC_GOOGLE_MAPS_API_KEY` prop to the component if you initialise the loader in a parent component.-->
|
|
118
|
+
<PlaceAutocomplete onResponse={...} onError={...} />
|
|
119
|
+
|
|
120
|
+
<!-- You can now use other Google Maps services, e.g., a map -->
|
|
121
|
+
<div id="map"></div>
|
|
122
|
+
```
|
|
123
|
+
|
|
54
124
|
## Security
|
|
55
125
|
|
|
56
126
|
### API Key Security
|
|
@@ -60,7 +130,7 @@ Your Google Maps API Key is a sensitive credential. To prevent unauthorised use
|
|
|
60
130
|
1. Go to the [Google Cloud Console](https://console.cloud.google.com/google/maps-apis/credentials).
|
|
61
131
|
2. Select your API key.
|
|
62
132
|
3. Under **Application restrictions**, select **HTTP referrers (web sites)** and add your application's domain(s) (e.g., `your-domain.com/*`).
|
|
63
|
-
4. Under **API restrictions**, select **Restrict key** and choose
|
|
133
|
+
4. Under **API restrictions**, select **Restrict key** and choose the APIs you are using (e.g., **Places API**, **Maps JavaScript API**).
|
|
64
134
|
|
|
65
135
|
### XSS Protection
|
|
66
136
|
|
|
@@ -72,91 +142,13 @@ This component is built to be accessible and follows the [WAI-ARIA Authoring Pra
|
|
|
72
142
|
|
|
73
143
|
* **Keyboard Navigation:** Users can navigate suggestions using `ArrowUp`, `ArrowDown`, select with `Enter`, and close the list with `Escape`.
|
|
74
144
|
* **Screen Reader Support:** Uses `role="combobox"`, `aria-autocomplete`, `aria-expanded`, and `aria-activedescendant` to provide a clear experience for screen reader users.
|
|
75
|
-
* **Focus Management:** Focus remains on the input field while navigating the suggestion list
|
|
76
|
-
|
|
77
|
-
## Basic Usage
|
|
78
|
-
|
|
79
|
-
1. Replace `___YOUR_API_KEY___` with your actual **Google Maps API Key**.
|
|
80
|
-
2. Use the `onResponse` callback to **handle the response**.
|
|
81
|
-
|
|
82
|
-
```javascript
|
|
83
|
-
<script>
|
|
84
|
-
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
|
|
85
|
-
import type { PlaceResult, ComponentOptions, RequestParams } from 'places-autocomplete-svelte/interfaces';
|
|
86
|
-
|
|
87
|
-
// Get API Key securely (e.g., from environment variables)
|
|
88
|
-
const PUBLIC_GOOGLE_MAPS_API_KEY = import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY;
|
|
89
|
-
let fullResponse: PlaceResult | null = $state(null);
|
|
90
|
-
let placesError = $state('');
|
|
91
|
-
|
|
92
|
-
// --- Event Handlers ---
|
|
93
|
-
const handleResponse = (response: PlaceResult) => {
|
|
94
|
-
console.log('Place Selected:', response);
|
|
95
|
-
fullResponse = response;
|
|
96
|
-
placesError = ''; // Clear previous errors
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const handleError = (error: string) => {
|
|
100
|
-
console.error('Places Autocomplete Error:', error);
|
|
101
|
-
placesError = error;
|
|
102
|
-
fullResponse = null; // Clear previous results
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// --- Configuration (Optional) ---
|
|
106
|
-
const requestParams: Partial<RequestParams> = $state({
|
|
107
|
-
region: 'GB',
|
|
108
|
-
language: 'en-GB',
|
|
109
|
-
});
|
|
110
|
-
const fetchFields: string[] = $state(['formattedAddress', 'addressComponents', 'displayName']);
|
|
111
|
-
const options: Partial<ComponentOptions> = $state({
|
|
112
|
-
placeholder: 'Start typing your address...',
|
|
113
|
-
debounce: 200,
|
|
114
|
-
classes: {
|
|
115
|
-
input: 'my-custom-input-class border-blue-500',
|
|
116
|
-
highlight: 'bg-yellow-200 text-black',
|
|
117
|
-
},
|
|
118
|
-
clear_input: false,
|
|
119
|
-
});
|
|
120
|
-
</script>
|
|
121
|
-
|
|
122
|
-
{#if placesError}
|
|
123
|
-
<div class="error-message" role="alert">
|
|
124
|
-
Error: {placesError}
|
|
125
|
-
</div>
|
|
126
|
-
{/if}
|
|
127
|
-
|
|
128
|
-
<PlaceAutocomplete
|
|
129
|
-
{PUBLIC_GOOGLE_MAPS_API_KEY}
|
|
130
|
-
{requestParams}
|
|
131
|
-
{fetchFields}
|
|
132
|
-
{options}
|
|
133
|
-
onResponse={handleResponse}
|
|
134
|
-
onError={handleError}
|
|
135
|
-
/>
|
|
136
|
-
|
|
137
|
-
{#if fullResponse}
|
|
138
|
-
<h2>Selected Place Details:</h2>
|
|
139
|
-
<pre>{JSON.stringify(fullResponse, null, 2)}</pre>
|
|
140
|
-
{/if}
|
|
141
|
-
|
|
142
|
-
<style>
|
|
143
|
-
:global(.my-custom-input-class) {
|
|
144
|
-
padding: 0.75rem;
|
|
145
|
-
border-radius: 0.25rem;
|
|
146
|
-
width: 100%;
|
|
147
|
-
}
|
|
148
|
-
.error-message {
|
|
149
|
-
color: red;
|
|
150
|
-
margin-bottom: 1rem;
|
|
151
|
-
}
|
|
152
|
-
</style>
|
|
153
|
-
```
|
|
145
|
+
* **Focus Management:** Focus remains on the input field while navigating the suggestion list.
|
|
154
146
|
|
|
155
147
|
## Props
|
|
156
148
|
|
|
157
149
|
| Prop | Type | Required | Default | Description |
|
|
158
150
|
| :--- | :--- | :--- | :--- | :--- |
|
|
159
|
-
| `PUBLIC_GOOGLE_MAPS_API_KEY` | `string` | Yes | - | Your restricted Google Maps API Key. |
|
|
151
|
+
| `PUBLIC_GOOGLE_MAPS_API_KEY` | `string` | Yes | - | Your restricted Google Maps API Key. Not required if the loader has been initialised in a parent component. |
|
|
160
152
|
| `fetchFields` | `string[]` | No | `['formattedAddress', 'addressComponents']` | Place Data Fields to request. **Affects API cost.** |
|
|
161
153
|
| `requestParams` | `Partial<RequestParams>` | No | `{ inputOffset: 3, ... }` | Parameters for the Autocomplete API request. |
|
|
162
154
|
| `options` | `Partial<ComponentOptions>` | No | `{ debounce: 100, ... }` | Options to control component behavior and appearance. |
|
|
@@ -177,7 +169,8 @@ Get a reference to the component instance using `bind:this` to call its methods
|
|
|
177
169
|
|
|
178
170
|
<PlaceAutocomplete bind:this={autocompleteComponent} ... />
|
|
179
171
|
|
|
180
|
-
<button onclick={() => autocompleteComponent
|
|
172
|
+
<button onclick={() => autocompleteComponent?.clear()}>Clear</button>
|
|
173
|
+
<button onclick={() => autocompleteComponent?.focus()}>Focus</button>
|
|
181
174
|
```
|
|
182
175
|
|
|
183
176
|
| Method | Signature | Description |
|
|
@@ -248,7 +241,7 @@ const options = {
|
|
|
248
241
|
|
|
249
242
|
## TypeScript
|
|
250
243
|
|
|
251
|
-
This component is written in TypeScript. Import types from `places-autocomplete-svelte/interfaces`.
|
|
244
|
+
This component is written in TypeScript. Import types from `places-autocomplete-svelte/interfaces` and helpers from `places-autocomplete-svelte/gmaps`.
|
|
252
245
|
|
|
253
246
|
## Google Places API & Billing
|
|
254
247
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
|
-
import * as GMaps from '@googlemaps/js-api-loader';
|
|
4
3
|
import type { PlaceResult, Props } from './interfaces.js';
|
|
4
|
+
import { getGMapsContext, hasGMapsContext, importLibrary, initialiseGMapsNoContext, type GMapsContext } from './gmaps.js';
|
|
5
5
|
import {
|
|
6
6
|
validateOptions,
|
|
7
7
|
validateRequestParams,
|
|
@@ -10,14 +10,22 @@
|
|
|
10
10
|
createHighlightedSegments,
|
|
11
11
|
debounce
|
|
12
12
|
} from './helpers.js';
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
let gmaps: GMapsContext | undefined;
|
|
16
|
+
// Get the context synchronously. This is safe.
|
|
17
|
+
if (hasGMapsContext()) {
|
|
18
|
+
gmaps = getGMapsContext();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
14
22
|
|
|
15
23
|
let {
|
|
16
24
|
/**
|
|
17
25
|
* By default using SKU: Place Detals (Location Only) - 0.005 USD per each
|
|
18
26
|
* @see https://developers.google.com/maps/documentation/javascript/usage-and-billing#location-placedetails
|
|
19
27
|
*/
|
|
20
|
-
PUBLIC_GOOGLE_MAPS_API_KEY,
|
|
28
|
+
PUBLIC_GOOGLE_MAPS_API_KEY='',
|
|
21
29
|
fetchFields,
|
|
22
30
|
options,
|
|
23
31
|
onResponse = $bindable((response: PlaceResult) => {}),
|
|
@@ -35,16 +43,12 @@
|
|
|
35
43
|
fetchFields = validateFetchFields(fetchFields);
|
|
36
44
|
//console.log(fetchFields);
|
|
37
45
|
|
|
38
|
-
// variable to hold a reference to the component's root element
|
|
39
|
-
//let componentRoot: HTMLElement;
|
|
40
|
-
|
|
41
46
|
let kbdAction = $state(''); // 'up', 'down', or 'escape'
|
|
42
47
|
|
|
43
48
|
// Local variables
|
|
44
49
|
let inputRef: HTMLInputElement;
|
|
45
50
|
let currentSuggestion = $state(-1);
|
|
46
51
|
let results: any[] = $state([]);
|
|
47
|
-
let loader: GMaps.Loader;
|
|
48
52
|
let placesApi: { [key: string]: any } = {};
|
|
49
53
|
|
|
50
54
|
//https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data
|
|
@@ -107,7 +111,6 @@
|
|
|
107
111
|
return request;
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
|
|
111
114
|
/**
|
|
112
115
|
* Make request and get autocomplete suggestions.
|
|
113
116
|
* @param event
|
|
@@ -215,6 +218,8 @@
|
|
|
215
218
|
* Initialize the Google Maps JavaScript API Loader.
|
|
216
219
|
*/
|
|
217
220
|
onMount(async (): Promise<void> => {
|
|
221
|
+
|
|
222
|
+
|
|
218
223
|
if (isDefaultOnResponse) {
|
|
219
224
|
console.warn(
|
|
220
225
|
'PlaceAutocomplete: The `onResponse` callback has not been provided. Selected place data will not be handled. See documentation for usage.'
|
|
@@ -225,25 +230,34 @@
|
|
|
225
230
|
inputRef.focus();
|
|
226
231
|
}
|
|
227
232
|
|
|
228
|
-
// load the Google Maps API
|
|
229
233
|
try {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
// Await the promise that was stored in the context by the parent.
|
|
235
|
+
// If the parent has already finished, this resolves immediately.
|
|
236
|
+
// If the parent is still loading, this will wait.
|
|
237
|
+
if(typeof gmaps !== 'undefined' && gmaps) {
|
|
238
|
+
await gmaps?.initializationPromise;
|
|
239
|
+
}else{
|
|
240
|
+
|
|
241
|
+
// Check if the API key is provided
|
|
242
|
+
if(PUBLIC_GOOGLE_MAPS_API_KEY === '' || !PUBLIC_GOOGLE_MAPS_API_KEY) {
|
|
243
|
+
throw new Error('Google Maps API key is required. Please provide a valid API key.');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// No context available, initialize without context
|
|
247
|
+
// This will load the Google Maps script
|
|
248
|
+
// and set up the necessary objects
|
|
249
|
+
// for places API usage.
|
|
250
|
+
await initialiseGMapsNoContext({key: PUBLIC_GOOGLE_MAPS_API_KEY, 'v': 'weekly'});
|
|
251
|
+
}
|
|
235
252
|
|
|
236
|
-
const { AutocompleteSessionToken, AutocompleteSuggestion } =
|
|
237
|
-
await loader.importLibrary('places');
|
|
253
|
+
const { AutocompleteSessionToken, AutocompleteSuggestion } = await importLibrary('places');
|
|
238
254
|
|
|
239
255
|
placesApi.AutocompleteSessionToken = AutocompleteSessionToken;
|
|
240
256
|
placesApi.AutocompleteSuggestion = AutocompleteSuggestion;
|
|
241
257
|
|
|
242
|
-
// const {Geocoder} = await loader.importLibrary("geocoding");
|
|
243
|
-
// placesApi.Geocoder = new Geocoder();
|
|
244
|
-
|
|
245
258
|
setSessionToken();
|
|
246
259
|
} catch (e: any) {
|
|
260
|
+
console.log(e);
|
|
247
261
|
onError(
|
|
248
262
|
(e.name || 'An error occurred') + ' - ' + (e.message || 'Error loading Google Maps API')
|
|
249
263
|
);
|
|
@@ -353,7 +367,6 @@
|
|
|
353
367
|
]}
|
|
354
368
|
onmouseenter={() => (currentSuggestion = i)}
|
|
355
369
|
id="option-{i + 1}"
|
|
356
|
-
|
|
357
370
|
>
|
|
358
371
|
<button
|
|
359
372
|
type="button"
|
package/dist/gmaps.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Writable } from 'svelte/store';
|
|
2
|
+
import { importLibrary, type APIOptions } from "@googlemaps/js-api-loader";
|
|
3
|
+
interface GMapsContext {
|
|
4
|
+
isInitialized: Writable<boolean>;
|
|
5
|
+
error: Writable<Error | null>;
|
|
6
|
+
initializationPromise: Promise<void> | null;
|
|
7
|
+
}
|
|
8
|
+
export type { GMapsContext, APIOptions };
|
|
9
|
+
/**
|
|
10
|
+
* Creates and sets the Google Maps context with writable stores.
|
|
11
|
+
* This is synchronous and should be called once in a top-level component's script.
|
|
12
|
+
*/
|
|
13
|
+
export declare function setGMapsContext(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Retrieves the shared Google Maps context.
|
|
16
|
+
* @returns {GMapsContext} The stores for initialization status and errors.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getGMapsContext(): GMapsContext;
|
|
19
|
+
export declare function hasGMapsContext(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Asynchronously initializes the Google Maps loader using the provided context.
|
|
22
|
+
* This function is idempotent and safe to be called multiple times.
|
|
23
|
+
* @param context - The GMapsContext object.
|
|
24
|
+
* @param options - The options for the JS API loader, including your API key.
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
27
|
+
export declare function initialiseGMaps(options: APIOptions): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Initializes the Google Maps API without using the context.
|
|
30
|
+
* @param options The options for the JS API loader, including your API key.
|
|
31
|
+
* @returns A promise that resolves when the API is initialized.
|
|
32
|
+
*/
|
|
33
|
+
export declare function initialiseGMapsNoContext(options: APIOptions): Promise<void>;
|
|
34
|
+
export { importLibrary };
|
package/dist/gmaps.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { writable, get } from 'svelte/store';
|
|
3
|
+
import { setOptions, importLibrary } from "@googlemaps/js-api-loader";
|
|
4
|
+
const LOADER_CONTEXT_KEY = Symbol('gmaps-loader');
|
|
5
|
+
/**
|
|
6
|
+
* Creates and sets the Google Maps context with writable stores.
|
|
7
|
+
* This is synchronous and should be called once in a top-level component's script.
|
|
8
|
+
*/
|
|
9
|
+
export function setGMapsContext() {
|
|
10
|
+
// Only set the context if it doesn't already exist.
|
|
11
|
+
if (getContext(LOADER_CONTEXT_KEY))
|
|
12
|
+
return;
|
|
13
|
+
setContext(LOADER_CONTEXT_KEY, {
|
|
14
|
+
isInitialized: writable(false),
|
|
15
|
+
error: writable(null),
|
|
16
|
+
initializationPromise: null, // Will be set by the loader function
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves the shared Google Maps context.
|
|
21
|
+
* @returns {GMapsContext} The stores for initialization status and errors.
|
|
22
|
+
*/
|
|
23
|
+
export function getGMapsContext() {
|
|
24
|
+
const context = getContext(LOADER_CONTEXT_KEY);
|
|
25
|
+
if (!context) {
|
|
26
|
+
throw new Error('Google Maps context not found. Call setGMapsContext in a parent component.');
|
|
27
|
+
}
|
|
28
|
+
return context;
|
|
29
|
+
}
|
|
30
|
+
export function hasGMapsContext() {
|
|
31
|
+
const context = getContext(LOADER_CONTEXT_KEY);
|
|
32
|
+
return !!context;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Asynchronously initializes the Google Maps loader using the provided context.
|
|
36
|
+
* This function is idempotent and safe to be called multiple times.
|
|
37
|
+
* @param context - The GMapsContext object.
|
|
38
|
+
* @param options - The options for the JS API loader, including your API key.
|
|
39
|
+
* @returns {Promise<void>}
|
|
40
|
+
*/
|
|
41
|
+
export async function initialiseGMaps(options) {
|
|
42
|
+
// Get the context internally
|
|
43
|
+
const context = getGMapsContext();
|
|
44
|
+
// If the promise already exists, just await it. Don't re-initialize.
|
|
45
|
+
if (context.initializationPromise) {
|
|
46
|
+
return context.initializationPromise;
|
|
47
|
+
}
|
|
48
|
+
// If it's already marked as initialized (e.g., from a previous page navigation), resolve immediately.
|
|
49
|
+
if (get(context.isInitialized)) {
|
|
50
|
+
return Promise.resolve();
|
|
51
|
+
}
|
|
52
|
+
// Create the promise and store it in the context object.
|
|
53
|
+
context.initializationPromise = new Promise((resolve, reject) => {
|
|
54
|
+
try {
|
|
55
|
+
setOptions(options); // Await the setOptions which returns a promise
|
|
56
|
+
context.isInitialized.set(true);
|
|
57
|
+
resolve();
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
61
|
+
context.error.set(error);
|
|
62
|
+
console.error("Failed to set Google Maps API options.", error);
|
|
63
|
+
reject(error);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return context.initializationPromise;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Initializes the Google Maps API without using the context.
|
|
70
|
+
* @param options The options for the JS API loader, including your API key.
|
|
71
|
+
* @returns A promise that resolves when the API is initialized.
|
|
72
|
+
*/
|
|
73
|
+
export function initialiseGMapsNoContext(options) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
try {
|
|
76
|
+
setOptions(options);
|
|
77
|
+
resolve();
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
81
|
+
console.error("Failed to set Google Maps API options.", error);
|
|
82
|
+
reject(error);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Re-export importLibrary for components to use.
|
|
87
|
+
export { importLibrary };
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -48,6 +48,11 @@ export interface PlaceResult {
|
|
|
48
48
|
shortText: string;
|
|
49
49
|
types: string[];
|
|
50
50
|
}[];
|
|
51
|
+
location?: {
|
|
52
|
+
lat: number;
|
|
53
|
+
lng: number;
|
|
54
|
+
};
|
|
55
|
+
[key: string]: unknown;
|
|
51
56
|
}
|
|
52
57
|
export interface FormattedAddress {
|
|
53
58
|
street_number: string;
|
|
@@ -58,9 +63,10 @@ export interface FormattedAddress {
|
|
|
58
63
|
postcode: string;
|
|
59
64
|
}
|
|
60
65
|
export interface Props {
|
|
61
|
-
PUBLIC_GOOGLE_MAPS_API_KEY
|
|
66
|
+
PUBLIC_GOOGLE_MAPS_API_KEY?: string;
|
|
62
67
|
options?: ComponentOptions;
|
|
63
68
|
fetchFields?: string[];
|
|
69
|
+
libraries?: string[];
|
|
64
70
|
requestParams?: RequestParams;
|
|
65
71
|
onResponse: (response: PlaceResult) => void;
|
|
66
72
|
onError: (error: string) => void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "places-autocomplete-svelte",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.17",
|
|
5
5
|
"description": "A flexible, accessible, and secure Svelte component leveraging the Google Maps Places Autocomplete API (New) to provide a user-friendly way to search for and retrieve detailed address information.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"svelte",
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
"svelte": "./dist/interfaces.js",
|
|
53
53
|
"default": "./dist/interfaces.js"
|
|
54
54
|
},
|
|
55
|
+
"./gmaps": {
|
|
56
|
+
"types": "./dist/gmaps.d.ts",
|
|
57
|
+
"svelte": "./dist/gmaps.js"
|
|
58
|
+
},
|
|
55
59
|
".": {
|
|
56
60
|
"types": "./dist/index.d.ts",
|
|
57
61
|
"svelte": "./dist/index.js",
|
|
@@ -71,37 +75,38 @@
|
|
|
71
75
|
"svelte": "^5.0.0"
|
|
72
76
|
},
|
|
73
77
|
"devDependencies": {
|
|
74
|
-
"@sveltejs/adapter-auto": "^6.1.
|
|
75
|
-
"@sveltejs/adapter-cloudflare": "^7.2.
|
|
76
|
-
"@sveltejs/kit": "^2.
|
|
77
|
-
"@sveltejs/package": "^2.5.
|
|
78
|
-
"@sveltejs/vite-plugin-svelte": "^6.1
|
|
79
|
-
"@tailwindcss/postcss": "^4.1.
|
|
80
|
-
"@tailwindcss/typography": "^0.5.
|
|
81
|
-
"@tailwindcss/vite": "^4.1.
|
|
78
|
+
"@sveltejs/adapter-auto": "^6.1.1",
|
|
79
|
+
"@sveltejs/adapter-cloudflare": "^7.2.4",
|
|
80
|
+
"@sveltejs/kit": "^2.46.4",
|
|
81
|
+
"@sveltejs/package": "^2.5.4",
|
|
82
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
83
|
+
"@tailwindcss/postcss": "^4.1.14",
|
|
84
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
85
|
+
"@tailwindcss/vite": "^4.1.14",
|
|
82
86
|
"@types/eslint": "^9.6.1",
|
|
83
|
-
"@types/
|
|
87
|
+
"@types/google.maps": "^3.58.1",
|
|
88
|
+
"@types/node": "^24.7.1",
|
|
84
89
|
"autoprefixer": "^10.4.21",
|
|
85
|
-
"eslint": "^9.
|
|
90
|
+
"eslint": "^9.37.0",
|
|
86
91
|
"eslint-config-prettier": "^10.1.8",
|
|
87
|
-
"eslint-plugin-svelte": "^3.
|
|
88
|
-
"globals": "^16.
|
|
92
|
+
"eslint-plugin-svelte": "^3.12.4",
|
|
93
|
+
"globals": "^16.4.0",
|
|
89
94
|
"postcss": "^8.5.6",
|
|
90
95
|
"prettier": "^3.6.2",
|
|
91
96
|
"prettier-plugin-svelte": "^3.4.0",
|
|
92
|
-
"publint": "^0.3.
|
|
93
|
-
"svelte": "^5.
|
|
94
|
-
"svelte-check": "^4.3.
|
|
95
|
-
"tailwindcss": "^4.1.
|
|
97
|
+
"publint": "^0.3.14",
|
|
98
|
+
"svelte": "^5.39.11",
|
|
99
|
+
"svelte-check": "^4.3.3",
|
|
100
|
+
"tailwindcss": "^4.1.14",
|
|
96
101
|
"tslib": "^2.8.1",
|
|
97
|
-
"typescript": "^5.9.
|
|
98
|
-
"typescript-eslint": "^8.
|
|
99
|
-
"vite": "^7.1.
|
|
102
|
+
"typescript": "^5.9.3",
|
|
103
|
+
"typescript-eslint": "^8.46.0",
|
|
104
|
+
"vite": "^7.1.9"
|
|
100
105
|
},
|
|
101
106
|
"svelte": "./dist/index.js",
|
|
102
107
|
"types": "./dist/PlaceAutocomplete.svelte.d.ts",
|
|
103
108
|
"type": "module",
|
|
104
109
|
"dependencies": {
|
|
105
|
-
"@googlemaps/js-api-loader": "^
|
|
110
|
+
"@googlemaps/js-api-loader": "^2.0.1"
|
|
106
111
|
}
|
|
107
112
|
}
|