UNPKG

places-autocomplete-hook

Version:
1 lines 14.9 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\nimport {\n UsePlacesAutocompleteOptions,\n UsePlacesAutocompleteResult,\n PlacePrediction,\n PlaceDetails,\n AddressComponent,\n} from './types';\n\nexport function usePlacesAutocomplete({\n apiKey,\n debounceMs = 300,\n language = 'en',\n includedPrimaryTypes,\n includedRegionCodes,\n sessionToken,\n location,\n setSelectedPlace,\n}: UsePlacesAutocompleteOptions): UsePlacesAutocompleteResult {\n const [value, setValue] = useState('');\n const [predictions, setPredictions] = useState<PlacePrediction[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const debounceTimer = useRef<NodeJS.Timeout | null>(null);\n\n const clear = useCallback(() => {\n setPredictions([]);\n setError(null);\n setValue('');\n }, []);\n\n const extractAddressComponent = (\n components: AddressComponent[],\n type: string,\n ): string | undefined => {\n // Safely find component by type, handling cases where types property is missing\n const component = components.find(\n comp => comp.types && Array.isArray(comp.types) && comp.types.includes(type),\n );\n return component?.longText;\n };\n\n const getPlaceDetails = useCallback(\n async (placeId: string, fields?: string[]): Promise<PlaceDetails> => {\n try {\n const defaultFields = ['formattedAddress', 'addressComponents', 'location'];\n const fieldMask = fields && fields.length > 0 ? fields.join(',') : defaultFields.join(',');\n\n const response = await fetch(\n `https://places.googleapis.com/v1/places/${placeId}?key=${apiKey}&languageCode=${language}${sessionToken ? `&sessionToken=${sessionToken}` : ''}`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Goog-FieldMask': fieldMask,\n },\n },\n );\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n const addressComponents = data.addressComponents || [];\n\n return {\n accessibilityOptions: data.accessibilityOptions,\n addressComponents,\n addressDescriptor: data.addressDescriptor,\n adrFormatAddress: data.adrFormatAddress,\n allowsDogs: data.allowsDogs,\n businessStatus: data.businessStatus,\n city: extractAddressComponent(addressComponents, 'locality'),\n country: extractAddressComponent(addressComponents, 'country'),\n curbsidePickup: data.curbsidePickup,\n currentOpeningHours: data.currentOpeningHours,\n delivery: data.delivery,\n dineIn: data.dineIn,\n displayName: data.displayName,\n editorialSummary: data.editorialSummary,\n formattedAddress: data.formattedAddress,\n goodForChildren: data.goodForChildren,\n goodForGroups: data.goodForGroups,\n goodForWatchingSports: data.goodForWatchingSports,\n googleMapsLinks: data.googleMapsLinks,\n googleMapsUri: data.googleMapsUri,\n iconBackgroundColor: data.iconBackground,\n iconMaskBaseUri: data.iconMaskBaseUri,\n internationalPhoneNumber: data.internationalPhoneNumber,\n liveMusic: data.liveMusic,\n location: data.location,\n menuForChildren: data.menuForChildren,\n name: data.name,\n nationalPhoneNumber: data.nationalPhoneNumber,\n outdoorSeating: data.outdoorSeating,\n parkingOptions: data.parkingOptions,\n paymentOptions: data.paymentOptions,\n photos: data.photos,\n placeId: data.id || placeId,\n plusCode: data.plusCode,\n postalAddress: data.postalAddress,\n postalCode: extractAddressComponent(addressComponents, 'postal_code'),\n priceLevel: data.priceLevel,\n priceRange: data.priceRange,\n primaryType: data.primaryType,\n primaryTypeDisplayName: data.primaryTypeDisplayName,\n pureServiceAreaBusiness: data.pureServiceAreaBusiness,\n rating: data.rating,\n regularOpeningHours: data.regularOpeningHours,\n reservable: data.reservable,\n restroom: data.restroom,\n reviews: data.reviews,\n servesBeer: data.servesBeer,\n servesCocktails: data.servesCocktails,\n servesDessert: data.servesDessert,\n servesDinner: data.servesDinner,\n servesLunch: data.servesLunch,\n servesWine: data.servesWine,\n shortFormattedAddress: data.shortFormattedAddress,\n state: extractAddressComponent(addressComponents, 'administrative_area_level_1'),\n streetName: extractAddressComponent(addressComponents, 'route'),\n streetNumber: extractAddressComponent(addressComponents, 'street_number'),\n takeout: data.takeout,\n timeZone: data.timeZone,\n types: data.types,\n userRatingCount: data.userRatingCount,\n utcOffsetMinutes: data.utcOffsetMinutes,\n viewport: data.viewport,\n websiteUri: data.websiteUri,\n };\n } catch (err) {\n throw err instanceof Error\n ? err\n : new Error('An error occurred while fetching place details');\n }\n },\n [apiKey, language, sessionToken],\n );\n\n const handlePlaceSelect = useCallback(\n async (placeId: string) => {\n setSelectedPlace?.(placeId);\n },\n [setSelectedPlace],\n );\n\n const search = useCallback(\n async (input: string) => {\n if (!input.trim()) {\n clear();\n return;\n }\n\n try {\n setLoading(true);\n setError(null);\n\n const requestBody: any = {\n input: input,\n languageCode: language,\n };\n\n if (includedPrimaryTypes) {\n requestBody.includedPrimaryTypes = includedPrimaryTypes;\n requestBody.includeQueryPredictions = true;\n }\n\n if (includedRegionCodes) {\n requestBody.includedRegionCodes = includedRegionCodes;\n }\n\n if (location) {\n requestBody.locationBias = {\n circle: {\n center: {\n latitude: location.lat,\n longitude: location.lng,\n },\n radius: location.radius,\n },\n };\n }\n\n if (includedPrimaryTypes || includedRegionCodes || location) {\n requestBody.includeQueryPredictions = true;\n }\n\n if (sessionToken) {\n requestBody.sessionToken = sessionToken;\n }\n\n // Build FieldMask - include query prediction fields when includeQueryPredictions is true\n let fieldMask =\n 'suggestions.placePrediction.place,suggestions.placePrediction.placeId,suggestions.placePrediction.text,suggestions.placePrediction.structuredFormat,suggestions.placePrediction.types';\n if (requestBody.includeQueryPredictions) {\n fieldMask +=\n ',suggestions.queryPrediction.text,suggestions.queryPrediction.structuredFormat';\n }\n\n const response = await fetch(\n `https://places.googleapis.com/v1/places:autocomplete?key=${apiKey}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Goog-FieldMask': fieldMask,\n },\n body: JSON.stringify(requestBody),\n },\n );\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n // Filter to only include place predictions (exclude query predictions)\n // This will throw if data.suggestions is undefined (maintains original error behavior)\n const placePredictions = data.suggestions\n .map((suggestion: any) => suggestion.placePrediction)\n .filter((prediction: any) => prediction !== undefined && prediction !== null);\n setPredictions(placePredictions);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('An error occurred'));\n setPredictions([]);\n } finally {\n setLoading(false);\n }\n },\n [apiKey, language, sessionToken, includedPrimaryTypes, includedRegionCodes, location, clear],\n );\n\n const debouncedSearch = useCallback(\n async (input: string) => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n return new Promise<void>(resolve => {\n debounceTimer.current = setTimeout(async () => {\n await search(input);\n resolve();\n }, debounceMs);\n });\n },\n [search, debounceMs],\n );\n\n useEffect(() => {\n return () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n };\n }, []);\n\n return {\n value,\n suggestions: {\n status: error\n ? 'ERROR'\n : loading\n ? 'LOADING'\n : predictions.length > 0\n ? 'OK'\n : 'ZERO_RESULTS',\n data: predictions,\n },\n setValue: (newValue: string, shouldFetchData = true) => {\n setValue(newValue);\n if (shouldFetchData) {\n debouncedSearch(newValue);\n }\n },\n clearSuggestions: clear,\n search: debouncedSearch,\n loading,\n error,\n getPlaceDetails,\n handlePlaceSelect,\n };\n}\n\nexport type {\n PlacePrediction,\n AddressComponent,\n UsePlacesAutocompleteOptions,\n UsePlacesAutocompleteResult,\n PlaceDetails,\n};\n"],"mappings":"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","names":[]}