places-autocomplete-svelte
Version:
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.
88 lines (87 loc) • 3.27 kB
JavaScript
import { getContext, setContext } from 'svelte';
import { writable, get } from 'svelte/store';
import { setOptions, importLibrary } from "@googlemaps/js-api-loader";
const LOADER_CONTEXT_KEY = Symbol('gmaps-loader');
/**
* Creates and sets the Google Maps context with writable stores.
* This is synchronous and should be called once in a top-level component's script.
*/
export function setGMapsContext() {
// Only set the context if it doesn't already exist.
if (getContext(LOADER_CONTEXT_KEY))
return;
setContext(LOADER_CONTEXT_KEY, {
isInitialized: writable(false),
error: writable(null),
initializationPromise: null, // Will be set by the loader function
});
}
/**
* Retrieves the shared Google Maps context.
* @returns {GMapsContext} The stores for initialization status and errors.
*/
export function getGMapsContext() {
const context = getContext(LOADER_CONTEXT_KEY);
if (!context) {
throw new Error('Google Maps context not found. Call setGMapsContext in a parent component.');
}
return context;
}
export function hasGMapsContext() {
const context = getContext(LOADER_CONTEXT_KEY);
return !!context;
}
/**
* Asynchronously initializes the Google Maps loader using the provided context.
* This function is idempotent and safe to be called multiple times.
* @param context - The GMapsContext object.
* @param options - The options for the JS API loader, including your API key.
* @returns {Promise<void>}
*/
export async function initialiseGMaps(options) {
// Get the context internally
const context = getGMapsContext();
// If the promise already exists, just await it. Don't re-initialize.
if (context.initializationPromise) {
return context.initializationPromise;
}
// If it's already marked as initialized (e.g., from a previous page navigation), resolve immediately.
if (get(context.isInitialized)) {
return Promise.resolve();
}
// Create the promise and store it in the context object.
context.initializationPromise = new Promise((resolve, reject) => {
try {
setOptions(options); // Await the setOptions which returns a promise
context.isInitialized.set(true);
resolve();
}
catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
context.error.set(error);
console.error("Failed to set Google Maps API options.", error);
reject(error);
}
});
return context.initializationPromise;
}
/**
* Initializes the Google Maps API without using the context.
* @param options The options for the JS API loader, including your API key.
* @returns A promise that resolves when the API is initialized.
*/
export function initialiseGMapsNoContext(options) {
return new Promise((resolve, reject) => {
try {
setOptions(options);
resolve();
}
catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
console.error("Failed to set Google Maps API options.", error);
reject(error);
}
});
}
// Re-export importLibrary for components to use.
export { importLibrary };