UNPKG

places-autocomplete-js

Version:

Google Maps Platform Awards 2025 Winner - A flexible, accessible, and secure vanilla JavaScript library that brings the power of Google's address search to your application in minutes.

1 lines 60 kB
{"version":3,"file":"places-autocomplete.umd.cjs","sources":["../PlacesAutocomplete.js"],"sourcesContent":["import \"./places-autocomplete.css\";\n\n/**\n * A flexible and customisable Places Autocomplete widget powered by the Google Maps Places API.\n *\n * Features:\n * - Dynamic Google Maps API loading\n * - Debounced input for optimized API requests\n * - Keyboard navigation (Arrow keys, Enter, Escape)\n * - Session token management for cost-effective API usage\n * - Customizable styling and behavior\n *\n * @author Alexander Pechkarev <alexpechkarev@gmail.com>\n * @license MIT\n */\nexport class PlacesAutocomplete {\n  // --- Private Properties (using # or _ prefix by convention) ---\n  #containerId; // Container ID where the autocomplete widget will be rendered.\n  #pacEl;\n  #googleMapsApiKey;\n  #googleMapsApiVersion;\n  #options;\n  #request;\n  #inputElement;\n  #container;\n  #ul;\n  #kbdEscape;\n  #kbdUp;\n  #kbdDown;\n  #allSuggestions = [];\n  #currentSuggestion = -1;\n  #onDataCallback; // For user-provided data callback\n  #onErrorCallback; // For user-provided error callback\n  #debouncedMakeAcRequest; // Declare without initializing here\n  #defaultOptions = {\n    // Default options for the autocomplete widget.\n    autofocus: false, // Automatically focus the input on load.\n    autocomplete: \"off\", // HTML autocomplete attribute for the input.\n    placeholder: \"Start typing your address ...\", // Placeholder text for the input.\n    distance: true, // Show distance in suggestions (requires origin in request).\n    distance_units: \"km\", // Units for distance ('km' or 'miles').\n    label: \"\", // Optional label text above the input.\n    debounce: 100, // Debounce delay (ms) for API requests.\n    clear_input: true, // Clear input button (not implemented in this version).\n    debug: false, // Enable debug mode (not implemented in this version).\n  };\n  #defaultClasses = {\n    // CSS classes for various parts of the widget.\n    section: \"\", // Outer section container.\n    container: \"pac-container\", // \"relative z-10 transform rounded-xl mt-4\", // Main container div.\n    icon_container: \"pac-icon-container\", //\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\", // Container for the search icon.\n    icon: '<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"pac-w-5 pac-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>', // SVG for the search icon.\n    input: \"pac-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\", // Input field.\n    kbd_container: \"pac-kbd-container\", //\"absolute inset-y-0 right-0 flex py-1.5 pr-1.5\", // Container for keyboard hints.\n    kbd_escape: \"pac-kbd-escape\", //\"inline-flex items-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-8 mr-1\", // Escape key hint.\n    kbd_up: \"pac-kbd-up\", //\"inline-flex items-center justify-center rounded border border-gray-300 px-1 font-sans text-xs text-gray-500 w-6\", // Up arrow key hint.\n    kbd_down: \"pac-kbd-down\", //\"inline-flex items-center rounded border border-gray-400 px-1 font-sans text-xs text-gray-500 justify-center w-6\", // Down arrow key hint.\n    kbd_active: \"pac-kbd-active\", //\"bg-indigo-500 text-white\", // Class for active keyboard hint.\n    ul: \"pac-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\", // Suggestions list (ul).\n    li: \"pac-li\", //\"z-50 cursor-default select-none py-2 px-2 lg:px-4 text-gray-900 hover:bg-indigo-500 hover:text-white\", // Suggestion item (li).\n    li_current: \"pac-li-current\", //\"bg-indigo-500\", // Class for the currently selected suggestion item.\n    li_a: \"pac-li-a\", //\"block w-full flex justify-between\", // Link element within a suggestion item.\n    li_button: \"pac-li-button\", //\"block w-full flex justify-between\", // Link element within a suggestion item.\n    li_button_current: \"pac-li-button-current\", //\"text-white\", // Class for the link in the currently selected suggestion item.\n    li_div_container: \"pac-li-div-container\", //\"flex min-w-0 gap-x-4\", // Container div within the suggestion link.\n    li_div_p_container: \"pac-li-div-p-container\",\n    li_div_one: \"pac-li-div-one\", //\"min-w-0 flex-auto\", // First inner div (for place name).\n    li_div_one_p: \"pac-li-div-one-p\", //\"text-sm/6\", // Paragraph for the place name.\n    map_icon_svg: \"pac-map-icon-svg\",\n    map_pin_icon:\n      '<path d=\"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/>',\n    li_div_one_p_secondaryText: \"pac-li-div-one-p-secondaryText\",\n    li_div_two: \"pac-li-div-two\", //\"shrink-0 flex flex-col items-end min-w-16\", // Second inner div (for distance).\n    li_div_two_p: \"pac-li-div-two-p\", //\"mt-1 text-xs/5\", // Paragraph for the distance.\n    highlight: \"pac-highlight\", //\"font-bold\", // Class for highlighting matched text in suggestions.\n  };\n  #defaultRequestParams = {\n    // Default parameters for the autocomplete request.\n    input: \"\", // Initial input value (empty).\n    includedRegionCodes: [\"GB\"], // Default region codes to include in suggestions.\n    language: \"en-gb\",\n    region: \"GB\",\n  };\n  #fetchFields = [\"formattedAddress\", \"addressComponents\"];\n  #defaultFetchFields = [\"formattedAddress\", \"addressComponents\"]; // Fields to fetch for the selected place (can be extended).\n\n  /**\n   * Class constructor for PacAutocomplete.\n   * Initializes the autocomplete widget with the provided configuration.\n   * @param {Object} config - Configuration object for the autocomplete widget.\n   * @param {string} config.containerId - ID of the container element for the widget.\n   * @param {string} config.googleMapsApiKey - Google Maps API key.\n   * @param {string} [config.googleMapsApiVersion] - Version of the Google Maps API to use (default: \"weekly\").\n   * @param {Object} [config.options] - Additional options for the widget (e.g., classes, callbacks).\n   * @param {Object} [config.requestParams] - Parameters for the autocomplete request (e.g., input, region).\n   * @param {*} config\n   */\n  constructor(config) {\n    if (!config || !config.containerId || !config.googleMapsApiKey) {\n      throw new Error(\n        \"PacAutocomplete: Missing required configuration (containerId, googleMapsApiKey).\"\n      );\n    }\n\n    this.#containerId = config.containerId; // Store the configuration object\n    this.#pacEl = document.getElementById(config.containerId);\n    if (!this.#pacEl) {\n      throw new Error(\n        `PacAutocomplete: Container element with ID \"${config.containerId}\" not found.`\n      );\n    }\n\n    this.#googleMapsApiKey = config.googleMapsApiKey;\n    this.#googleMapsApiVersion = config.googleMapsApiVersion || \"weekly\";\n\n    // Merge user options with defaults\n    this.#options = {\n      ...this.#defaultOptions, // Default options\n      ...config.options, // User-defined options override defaults\n    };\n    // Ensure classes are deeply merged if user provides partial classes\n    if (config.options && config.options.classes) {\n      this.#options.classes = {\n        ...this.#defaultClasses,\n        ...config.options.classes,\n      };\n    } else {\n      this.#options.classes = this.#defaultClasses; // Use default classes if none provided\n    }\n\n    if (this.#options.debug) {\n      console.log(\"___debug constructor options:\");\n      console.log(this.#options);\n    }\n\n    if (config.fetchFields && Array.isArray(config.fetchFields)) {\n      this._setFetchFields(config.fetchFields); // Set fetch fields from user config\n    }\n\n    // Set default response and error callbacks if not provided\n    this.#onDataCallback =\n      config.onResponse ||\n      ((place) => {\n        console.info(\"---------Default onResponse not provided---------\");\n        console.info(\"Selected Place:\", JSON.stringify(place, null, 2));\n      });\n    this.#onErrorCallback =\n      config.onError ||\n      ((error) => {\n        console.error(\"---------Default onError not provided---------\");\n        console.error(\"PAC Error:\", error);\n      });\n\n    if (config.requestParams && Object.keys(config.requestParams).length > 0) {\n      this.#request = {\n        ...this.#defaultRequestParams,\n        ...config.requestParams,\n      };\n    } else {\n      this.#request = { ...this.#defaultRequestParams }; // Use defaults if no requestParams provided\n    }\n\n    if (this.#options.debug) {\n      console.log(\"___debug constructor requestParams:\", this.#request);\n    }\n\n    this._initialiseDebouncedRequest(); // Initialize the debounced request function\n\n    this._init(); // Underscore prefix for internal initialization method\n  }\n\n  // --- Private Initialization Method ---\n  async _init() {\n    try {\n      // check if google maps api is already loaded\n      if (typeof google === \"undefined\" || !google.maps) {\n        // Load the Google Maps API dynamically\n        await this._loadGoogleMapsApi({\n          key: this.#googleMapsApiKey,\n          v: this.#googleMapsApiVersion,\n        });\n      }\n      this._createPACStructure();\n      await this._initializeAutocomplete();\n    } catch (error) {\n      this.#onErrorCallback(error);\n    }\n  }\n\n  /**\n   * Initializes the debounced request function for fetching autocomplete suggestions.\n   * This function is called on initialization and when options are updated.\n   *\n   * @private\n   */\n  _initialiseDebouncedRequest() {\n    this.#debouncedMakeAcRequest = this._debounce(async () => {\n      if (!this.#inputElement || !this.#inputElement.value) {\n        this._reset();\n        if (this.#inputElement)\n          this.#inputElement.setAttribute(\"aria-expanded\", \"false\");\n        return;\n      }\n\n      this.#request.input = this.#inputElement.value;\n\n      try {\n        const { suggestions } =\n          // eslint-disable-next-line no-undef\n          await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(\n            this.#request\n          );\n\n        // Display suggestions\n        if (suggestions && suggestions.length > 0) {\n          const suggestionElements = await Promise.all(\n            this._createSuggestionElements(suggestions)\n          );\n          this.#ul.replaceChildren(...suggestionElements);\n          this.#ul.style.display = \"block\";\n          this.#inputElement.setAttribute(\"aria-expanded\", \"true\");\n        } else {\n          // No suggestions found\n          this._reset(); // Clear any old suggestions\n          this.#inputElement.setAttribute(\"aria-expanded\", \"false\");\n          // Optionally display a \"no results\" message in the 'ul'\n        }\n      } catch (error) {\n        this.#onErrorCallback(error);\n        this._reset();\n      }\n    }, this.#options.debounce); // Default debounce to 100ms if not set\n  }\n  /**\n   * Sets the fields to fetch for the selected place.\n   * @param {Array<string>} fields - The fields to fetch.\n   */\n  _setFetchFields(fields) {\n    if (Array.isArray(fields) && fields.length > 0) {\n      this.#fetchFields = [\n        ...new Set([...this.#defaultFetchFields, ...fields]),\n      ].filter((e) => e); // Ensure unique and non-empty fields\n    }\n  }\n\n  /**\n   * Creates a debounced version of a function.\n   *\n   * @private\n   * @param {Function} func - The function to debounce\n   * @param {number} wait - Delay in milliseconds\n   * @returns {Function} Debounced function\n   */\n  _debounce(func, wait) {\n    let timeout = null;\n    return function executedFunction(...args) {\n      const later = () => {\n        timeout = null;\n        func(...args); // Call original function\n      };\n      if (timeout !== null) {\n        clearTimeout(timeout); // Clear the previous timeout\n      }\n      timeout = setTimeout(later, wait ?? 100); // Set the new timeout\n    };\n  }\n  /**\n   * Formats a distance value from meters to the specified unit.\n   *\n   * @private\n   * @param {number|null|undefined} distance - Distance in meters\n   * @param {('km'|'miles')} units - Target unit for conversion\n   * @returns {string|null} Formatted distance string or null if invalid\n   */\n  _formatDistance(distance, units) {\n    if (typeof distance !== \"number\" || !this.#options.distance) {\n      return null; // Return null if distance isn't shown or invalid\n    }\n    let value;\n    let unitLabel;\n    if (units === \"km\") {\n      value = (distance / 1000).toFixed(2);\n      unitLabel = \"km\";\n    } else {\n      // Default to miles if not 'km'\n      value = (distance / 1609.34).toFixed(2);\n      unitLabel = \"miles\";\n    }\n    // Avoid showing \".00\"\n    value = value.replace(/\\.00$/, \"\");\n    return `${value} ${unitLabel}`;\n  }\n\n  /**\n   * Dynamically loads the Google Maps JavaScript API using the importLibrary method.\n   * This is the standard approach recommended by Google.\n   * @see https://developers.google.com/maps/documentation/javascript/load-maps-js-api\n   * @param {object} g - Configuration object for the API loader (key, v, libraries, etc.).\n   */\n  async _loadGoogleMapsApi(g) {\n    var h, // Promise tracking API load\n      a, // Script element\n      k, // Loop variable for config keys\n      p = \"The Google Maps JavaScript API\", // Error message prefix\n      c = \"google\", // Global namespace\n      l = \"importLibrary\", // Loader function name\n      q = \"__ib__\", // Internal callback name\n      m = document, // Document reference\n      b = window; // Window reference\n    b = b[c] || (b[c] = {}); // Ensure google namespace exists\n    var d = b.maps || (b.maps = {}), // Ensure google.maps namespace exists\n      r = new Set(), // Set to track requested libraries\n      e = new URLSearchParams(), // URL parameters for the API script\n      u = () =>\n        // Function to initiate API loading (if not already started)\n        h ||\n        // eslint-disable-next-line no-async-promise-executor\n        (h = new Promise(async (f, n) => {\n          // Create script element (done async to potentially wait for nonce)\n          // await (a = m.createElement('script')); // Original Google code had await here, might not be needed\n          a = m.createElement(\"script\"); // Create script tag\n          e.set(\"libraries\", [...r].join(\",\")); // Add accumulated libraries\n          // Add other parameters from the config object 'g'\n          for (k in g)\n            e.set(\n              k.replace(/[A-Z]/g, (t) => \"_\" + t[0].toLowerCase()), // Convert camelCase to snake_case\n              g[k]\n            );\n          e.set(\"callback\", c + \".maps.\" + q); // Set the internal callback function name\n          a.src = `https://maps.${c}apis.com/maps/api/js?` + e; // Construct the API URL\n          d[q] = f; // Assign the promise resolver to the callback name on google.maps\n          // Error handling for script loading failure\n          a.onerror = () =>\n            (h = n(\n              new Error(\n                `${p} could not load. Check your API key and network connection.`\n              )\n            )); // Use onerror for load failures\n          // Nonce for Content Security Policy\n          a.nonce = m.querySelector(\"script[nonce]\")?.nonce || \"\";\n          m.head.append(a); // Append the script to the document head\n        }));\n    // Define or reuse the importLibrary function on google.maps\n    // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n    d[l]\n      ? console.warn(p + \" only loads once. Ignoring:\", g) // Warn if called again\n      : (d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n))); // The actual importLibrary implementation\n  }\n\n  // --- UI Creation ---\n  _createPACStructure() {\n    const section = document.createElement(\"section\");\n    section.className = this.#options.classes.section;\n\n    // Main container\n    this.#container = document.createElement(\"div\");\n    this.#container.className = this.#options.classes.container;\n    this.#container.setAttribute(\"id\", this.#containerId + \"-div\");\n    section.appendChild(this.#container);\n\n    // Icon\n    const iconContainer = document.createElement(\"div\");\n    iconContainer.className = this.#options.classes.icon_container;\n    this.#container.appendChild(iconContainer);\n    const icon = document.createElement(\"div\");\n    icon.innerHTML = this.#options.classes.icon;\n    iconContainer.appendChild(icon.firstElementChild); // Append the actual SVG element\n\n    // Input field\n    this.#inputElement = document.createElement(\"input\");\n    this.#inputElement.id = this.#containerId + \"-input\"; // Assign ID for label association\n    this.#inputElement.type = \"text\";\n    this.#inputElement.name = \"search\"; // Consider making name configurable\n    this.#inputElement.placeholder = this.#options.placeholder;\n    this.#inputElement.autocomplete = this.#options.autocomplete;\n    this.#inputElement.className = this.#options.classes.input;\n    this.#inputElement.setAttribute(\"role\", \"combobox\");\n    this.#inputElement.setAttribute(\"aria-autocomplete\", \"list\");\n    this.#inputElement.setAttribute(\"aria-expanded\", \"false\"); // Will be updated dynamically\n    this.#inputElement.setAttribute(\"aria-controls\", \"pacSuggestions\"); // Links to the suggestions list\n    this.#inputElement.setAttribute(\"aria-activedescendant\", \"\"); // Will be updated dynamically\n\n    if (this.#options.autofocus) {\n      this.#inputElement.autofocus = true;\n    }\n    if (this.#options.label) {\n      const label = document.createElement(\"label\");\n      label.htmlFor = this.#inputElement.id; // Correctly link label to input by ID\n      label.textContent = this.#options.label;\n      // Add label classes if needed from opts.classes\n      section.prepend(label); // Append label before input or adjust structure\n    }\n    this.#container.appendChild(this.#inputElement);\n\n    // Keyboard hints container\n    const kbdContainer = document.createElement(\"div\");\n    kbdContainer.className = this.#options.classes.kbd_container;\n    this.#kbdEscape = document.createElement(\"kbd\");\n    this.#kbdEscape.className = this.#options.classes.kbd_escape;\n    this.#kbdEscape.textContent = \"Esc\";\n    kbdContainer.appendChild(this.#kbdEscape);\n    this.#kbdUp = document.createElement(\"kbd\");\n    this.#kbdUp.className = this.#options.classes.kbd_up;\n    this.#kbdUp.innerHTML = \"&#8593;\"; // Up arrow HTML entity\n    kbdContainer.appendChild(this.#kbdUp);\n    this.#kbdDown = document.createElement(\"kbd\");\n    this.#kbdDown.className = this.#options.classes.kbd_down;\n    this.#kbdDown.innerHTML = \"&#8595;\"; // Down arrow HTML entity\n    kbdContainer.appendChild(this.#kbdDown);\n    this.#container.appendChild(kbdContainer);\n\n    // Suggestions list (initially hidden)\n    this.#ul = document.createElement(\"ul\");\n    this.#ul.id = \"pacSuggestions\"; // Must match aria-controls\n    this.#ul.className = this.#options.classes.ul;\n    this.#ul.style.display = \"none\";\n    this.#ul.setAttribute(\"role\", \"listbox\");\n    this.#ul.setAttribute(\"aria-labelledby\", this.#inputElement.id); // Link listbox to input for accessibility\n    this.#container.appendChild(this.#ul);\n\n    this.#pacEl.appendChild(section);\n    section.addEventListener(\"keydown\", this._onKeyDown.bind(this)); // Bind 'this'\n  }\n\n  /**\n   * Attaches event listeners to the input element for handling user input.\n   * This includes debounced input handling, focus/blur events, and keyboard navigation.\n   */\n  _attachedEventListeners() {\n    this.#inputElement.addEventListener(\"input\", this.#debouncedMakeAcRequest);\n    // Add focus/blur listeners if needed to manage suggestion visibility\n    this.#inputElement.addEventListener(\"blur\", () => {\n      // Delay hiding suggestions to allow click events on them\n      setTimeout(() => {\n        if (this.#ul && !this.#ul.contains(document.activeElement)) {\n          // Check if focus moved outside suggestions\n          this.#ul.style.display = \"none\";\n          this.#inputElement.setAttribute(\"aria-expanded\", \"false\");\n        }\n      }, 200); // Adjust delay as needed\n    });\n    this.#inputElement.addEventListener(\"focus\", () => {\n      // Potentially show suggestions again if input has value\n      if (this.#inputElement.value && this.#allSuggestions.length > 0) {\n        this.#ul.style.display = \"block\";\n        this.#inputElement.setAttribute(\"aria-expanded\", \"true\");\n      }\n    });\n  }\n\n  _detachEventListeners() {\n    // Remove event listeners, remove elements from DOM\n    if (this.#inputElement) {\n      this.#inputElement.removeEventListener(\n        \"input\",\n        this.#debouncedMakeAcRequest\n      );\n      // remove other listeners\n    }\n    if (this.#pacEl && this.#container) {\n      this.#pacEl.removeChild(this.#container.parentElement); // remove the whole section\n    }\n  }\n\n  /**\n   * Initializes the core autocomplete functionality after the API is loaded.\n   * Imports necessary libraries and sets up the input event listener.\n   */\n  async _initializeAutocomplete() {\n    try {\n      // Ensure the 'places' library is available via the dynamic loader\n      // eslint-disable-next-line no-undef\n      await google.maps.importLibrary(\"places\");\n      // console.log('Places library imported successfully.'); // For debugging\n\n      // Initial token generation\n      this._refreshToken();\n\n      // Attach the debounced request function to the input element's 'input' event\n      if (this.#inputElement) {\n        this._attachedEventListeners();\n      } else {\n        this.#onErrorCallback(\n          new Error(\"Input element not found during initialization.\")\n        );\n      }\n    } catch (error) {\n      console.error(\"Error initializing Google Places Autocomplete:\", error);\n      // eslint-disable-next-line no-undef\n      this.#onErrorCallback(\n        new Error(\"Google Maps Places library not available.\")\n      );\n    }\n  }\n\n  /**\n   * Resets the autocomplete input field, clears suggestions, and optionally refreshes the session token.\n   * @param {boolean} [refresh=false] - Whether to refresh the Google Places session token.\n   */\n  _reset(refresh = false, placeData = null) {\n    this.#currentSuggestion = -1;\n\n    /**\n     * If the input element exists and clear_input is false, set its value to the formatted address\n     * from placeData. Otherwise, clear the input value.\n     * This allows the user to keep the input value if they selected a place,\n     * but still allows clearing it if clear_input is true or placeData is not provided.\n     * @type {HTMLInputElement}\n     * @property {string} value - The value of the input element.\n     * @property {boolean} clear_input - Whether to clear the input value after selection.\n     * @property {Object} placeData - The data of the selected place, containing formattedAddress.\n     */\n    if (\n      this.#inputElement &&\n      this.#options.clear_input == false &&\n      placeData &&\n      placeData.formattedAddress\n    ) {\n      this.#inputElement.value = placeData.formattedAddress; // Set input value to formatted address\n    } else if (this.#inputElement) {\n      this.#inputElement.value = \"\";\n    }\n\n    if (this.#inputElement) {\n      this.#inputElement.setAttribute(\"aria-expanded\", \"false\");\n      this.#inputElement.setAttribute(\"aria-activedescendant\", \"\"); // Clear aria-activedescendant\n      this.#inputElement.blur();\n    }\n\n    this.#allSuggestions = [];\n    this.#currentSuggestion = -1;\n    if (this.#ul) {\n      this.#ul.innerHTML = \"\"; // Clear existing suggestions\n      this.#ul.style.display = \"none\";\n    }\n    if (refresh) {\n      this._refreshToken();\n    }\n  }\n  /**\n   * Removes the 'current' highlighting classes from all suggestion list items (li) and their links (a).\n   */\n  _resetLiClasses() {\n    if (!this.#ul) return;\n    Array.from(this.#ul.children).forEach((li) => {\n      this.#options.classes.li_current\n        .split(\" \")\n        .forEach((cl) => li.classList.remove(cl));\n      const link = li.querySelector(\"button\");\n      if (link) {\n        this.#options.classes.li_button_current\n          .split(\" \")\n          .forEach((cl) => link.classList.remove(cl));\n      }\n    });\n  }\n\n  /**\n   * Handles keyboard events (ArrowDown, ArrowUp, Enter, Escape) for navigating\n   * and selecting suggestions or closing the list.\n   * @param {KeyboardEvent} e - The keyboard event object.\n   */\n  _onKeyDown(e) {\n    this._resetLiClasses(); // Reset classes on any key press within the suggestions\n\n    if (e.key === \"Escape\") {\n      e.preventDefault();\n      // Visual feedback for key press\n      this.#options.classes.kbd_active\n        .split(\" \")\n        .forEach((cl) => this.#kbdEscape?.classList.add(cl));\n      setTimeout(\n        () =>\n          this.#options.classes.kbd_active\n            .split(\" \")\n            .forEach((cl) => this.#kbdEscape?.classList.remove(cl)),\n        300\n      );\n\n      this._reset(true); // Reset search input and results, refresh token\n    }\n\n    if (\n      !this.#allSuggestions.length ||\n      !this.#ul ||\n      this.#ul.style.display === \"none\"\n    )\n      return;\n\n    if (e.key === \"ArrowDown\") {\n      e.preventDefault(); // Prevent cursor movement in input\n      this.#currentSuggestion = Math.min(\n        this.#currentSuggestion + 1,\n        this.#allSuggestions.length - 1\n      );\n      if (this.#currentSuggestion < 0) this.#currentSuggestion = 0; // Handle case where it was -1\n\n      const currentLi = this.#ul.children.item(this.#currentSuggestion);\n      if (currentLi) {\n        const currentButton = currentLi.querySelector(\"button\");\n        this.#options.classes.li_current\n          .split(\" \")\n          .forEach((cl) => currentLi.classList.add(cl));\n        if (currentButton) {\n          this.#options.classes.li_button_current\n            .split(\" \")\n            .forEach((cl) => currentButton.classList.add(cl));\n        }\n        currentLi.scrollIntoView({ block: \"nearest\" }); // Ensure visible\n        this.#inputElement.setAttribute(\"aria-activedescendant\", currentLi.id); // Update aria-activedescendant\n      }\n\n      // Visual feedback for key press\n      this.#options.classes.kbd_active\n        .split(\" \")\n        .forEach((cl) => this.#kbdDown?.classList.add(cl));\n      setTimeout(\n        () =>\n          this.#options.classes.kbd_active\n            .split(\" \")\n            .forEach((cl) => this.#kbdDown?.classList.remove(cl)),\n        300\n      );\n    } else if (e.key === \"ArrowUp\") {\n      e.preventDefault(); // Prevent cursor movement in input\n      this.#currentSuggestion = Math.max(this.#currentSuggestion - 1, 0); // Stay at 0 if already there\n\n      const currentLi = this.#ul.children.item(this.#currentSuggestion);\n      if (currentLi) {\n        const currentButton = currentLi.querySelector(\"button\");\n        this.#options.classes.li_current\n          .split(\" \")\n          .forEach((cl) => currentLi.classList.add(cl));\n        if (currentButton) {\n          this.#options.classes.li_button_current\n            .split(\" \")\n            .forEach((cl) => currentButton.classList.add(cl));\n        }\n        currentLi.scrollIntoView({ block: \"nearest\" }); // Ensure visible\n      }\n\n      // Visual feedback for key press\n      this.#options.classes.kbd_active\n        .split(\" \")\n        .forEach((cl) => this.#kbdUp?.classList.add(cl));\n      setTimeout(\n        () =>\n          this.#options.classes.kbd_active\n            .split(\" \")\n            .forEach((cl) => this.#kbdUp?.classList.remove(cl)),\n        300\n      );\n    } else if (e.key === \"Enter\") {\n      e.preventDefault(); // Prevent form submission if applicable\n      if (\n        this.#currentSuggestion >= 0 &&\n        this.#currentSuggestion < this.#allSuggestions.length\n      ) {\n        this._onPlaceSelected(\n          this.#allSuggestions[this.#currentSuggestion].place\n        );\n        // Reset is handled within onPlaceSelected via reset(true)\n      }\n    }\n  }\n\n  // Helper function to find a specific address component\n  _getAddressComponent(response, type) {\n    return (\n      response.addressComponents?.find((c) => c.types.includes(type))\n        ?.longText || \"\"\n    );\n  }\n\n  // Helper function to get secondary text from address components\n  _getSecondaryText(place) {\n    const locality = this._getAddressComponent(place, \"locality\");\n    const adminArea = this._getAddressComponent(\n      place,\n      \"administrative_area_level_1\"\n    );\n    const postalCode = this._getAddressComponent(place, \"postal_code\");\n    const country = this._getAddressComponent(place, \"country\");\n    let components = [locality, adminArea, country, postalCode].filter(Boolean);\n    return components.join(\", \");\n  }\n\n  /**\n   * Creates a button element for a suggestion item.\n   * @returns {HTMLButtonElement} The created button element.\n   */\n  _createButtonElement(index) {\n    const button = document.createElement(\"button\");\n    button.tabIndex = index + 1;\n    button.className = this.#options.classes.li_button; // block w-full flex justify-between\n    return button;\n  }\n\n  /**\n   * Creates a div container for suggestion item.\n   * @returns {HTMLDivElement} The created div container element.\n   */\n  _createDivElement(className) {\n    // create div elements pac-li-div-container\n    const divContainer = document.createElement(\"div\");\n    if (className) {\n      divContainer.className = className;\n    }\n    return divContainer;\n  }\n\n  /**\n   * Creates an SVG element representing a map pin icon.\n   * @returns {SVGSVGElement} The created SVG element.\n   */\n  _createMapPinIconElement() {\n    const svgIcon = document.createElementNS(\n      \"http://www.w3.org/2000/svg\",\n      \"svg\"\n    );\n    svgIcon.setAttribute(\"xmlns\", \"http://www.w3.org/2000/svg\");\n    svgIcon.setAttribute(\"width\", \"24\");\n    svgIcon.setAttribute(\"height\", \"24\");\n    svgIcon.setAttribute(\"viewBox\", \"0 0 24 24\");\n    svgIcon.setAttribute(\"fill\", \"none\");\n    svgIcon.setAttribute(\"stroke\", \"currentColor\");\n    svgIcon.setAttribute(\"stroke-width\", \"2\");\n    svgIcon.setAttribute(\"stroke-linecap\", \"round\");\n    svgIcon.setAttribute(\"stroke-linejoin\", \"round\");\n    svgIcon.setAttribute(\"class\", this.#options.classes.map_icon_svg);\n    // insert map pin icon path\n    svgIcon.innerHTML = this.#options.classes.map_pin_icon;\n    return svgIcon;\n  }\n\n  /**\n   * Creates a paragraph (P) element with an optional class name.\n   * @param {string} className\n   * @returns {HTMLParagraphElement}\n   */\n  _createPElement(className) {\n    const p = document.createElement(\"p\");\n    if (className) {\n      p.className = className;\n    }\n    return p;\n  }\n  /**\n   * Creates an array of list item (LI) elements for the suggestions dropdown.\n   * Each LI contains a link (A) with the place prediction details and distance.\n   * Handles highlighting the matched parts of the suggestion text.\n   * @param {Array<google.maps.places.AutocompleteSuggestion>} suggestions - Array of suggestion objects from the API.\n   * @returns {Array<HTMLLIElement>} An array of LI elements to be added to the suggestions UL.\n   */\n  _createSuggestionElements(suggestions) {\n    this.#allSuggestions = []; // Reset before populating\n    // Map suggestions to LI elements\n    return suggestions.map(async (suggestion, index) => {\n      // Fetch place details for each suggestion\n      let place = suggestion.placePrediction.toPlace();\n      // Fetch address components for the place\n      await place.fetchFields({ fields: [\"addressComponents\"] });\n\n      const li = document.createElement(\"li\");\n      li.id = `option-${index + 1}`;\n      li.className = this.#options.classes.li;\n\n      // create button element\n      const button = this._createButtonElement(index);\n      // Add click event listener to handle selection\n      button.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        this._onPlaceSelected(suggestion.placePrediction.toPlace());\n      });\n\n      // create div elements pac-li-div-container\n      const divContainer = this._createDivElement(\n        this.#options.classes.li_div_container\n      );\n\n      // create li div one element - place name\n      const liDivOne = this._createDivElement(this.#options.classes.li_div_one);\n      const liDivTwo = this._createDivElement(this.#options.classes.li_div_two);\n      // create map pin icon element\n      const mapPinIcon = this._createMapPinIconElement();\n      // append map pin icon to liDivOne\n      liDivOne.appendChild(mapPinIcon);\n\n      // create div p container\n      const divPContainer = this._createDivElement(\n        this.#options.classes.li_div_p_container\n      );\n      // append div p container to liDivOne\n      liDivOne.appendChild(divPContainer);\n\n      // create p element - place name\n      const pOne = this._createPElement(this.#options.classes.li_div_one_p);\n      // create p element - secondary text\n      const pTwo = this._createPElement(\n        this.#options.classes.li_div_one_p_secondaryText\n      );\n      // create p element - distance\n      const pThree = this._createPElement(this.#options.classes.li_div_two_p);\n      pThree.textContent = this._formatDistance(\n        suggestion.placePrediction.distanceMeters,\n        this.#options.distance_units ?? \"km\"\n      );\n      // append p element to div p container\n      divPContainer.appendChild(pOne);\n      divPContainer.appendChild(pTwo);\n      liDivTwo.appendChild(pThree);\n\n      // append liDivOne to divContainer\n      divContainer.appendChild(liDivOne);\n      divContainer.appendChild(liDivTwo);\n      // append divContainer to button\n      button.appendChild(divContainer);\n      // append button to li\n      li.appendChild(button);\n\n      // get prediction text\n      const predictionText = suggestion.placePrediction.mainText;\n      const originalText = predictionText.text;\n      // Array of objects with startOffset, endOffset\n      const matches = predictionText.matches;\n\n      //Highlighting Logic\n      let lastIndex = 0;\n\n      // Sort matches just in case they aren't ordered (though they usually are)\n      matches.sort((a, b) => a.startOffset - b.startOffset);\n\n      // 1. Create the outer span\n      const outerSpan = document.createElement(\"span\");\n\n      // 2. Create the inner span for the bold part\n      const innerSpan = document.createElement(\"span\");\n      innerSpan.classList = this.#options.classes.highlight ?? \"font-bold\"; // Use the highlight class from options\n\n      for (const match of matches) {\n        // Append text before the current match\n        outerSpan.textContent += originalText.substring(\n          lastIndex,\n          match.startOffset\n        );\n\n        // Append the highlighted match segment\n        if (match.startOffset > 0) {\n          // check previous charter is space\n          const prevChar = originalText.charAt(match.startOffset - 1);\n          if (prevChar == \" \") {\n            innerSpan.textContent += \" \";\n          }\n        }\n        innerSpan.textContent += originalText.substring(\n          match.startOffset,\n          match.endOffset\n        );\n\n        // Update the last index processed\n        lastIndex = match.endOffset;\n      }\n\n      // 3. Create a text node for the remaining text\n      const remainingText = document.createTextNode(\n        originalText.substring(lastIndex)\n      );\n\n      // 4. Append the inner span and the text node to the outer span\n      outerSpan.appendChild(innerSpan);\n      outerSpan.appendChild(remainingText);\n\n      // 5. Append the outer span to the paragraph element\n      pOne.appendChild(outerSpan);\n\n      // set secondary text\n      const secondaryText = this._getSecondaryText(place);\n\n      if (secondaryText) {\n        pTwo.textContent = secondaryText;\n      }\n\n      this.#allSuggestions.push({\n        id: index + 1,\n        place: place,\n        mainText: suggestion.placePrediction.mainText.text,\n        secondaryText: this._getSecondaryText(place),\n        description: suggestion.placePrediction.toString(),\n      });\n\n      li.appendChild(button);\n      return li;\n    });\n  }\n\n  /**\n   * Handles the selection of a place. Fetches required fields\n   * (displayName, formattedAddress, addressComponents) and calls the\n   * user-defined `onPacData` callback.\n   * @param {google.maps.places.Place} place - The selected Place object.\n   */\n  async _onPlaceSelected(place) {\n    let data = null;\n    try {\n      // Fetch necessary details for the selected place\n      await place.fetchFields({\n        fields: this.#fetchFields, //[\"displayName\", \"formattedAddress\", \"addressComponents\"], // Add more fields as needed\n      });\n      // Call the user-provided callback with the place data\n      // eslint-disable-next-line no-undef\n      data = place.toJSON(); // Convert to plain JSON object\n      this.#onDataCallback(data); // Convert to plain JSON object for the callback\n    } catch (error) {\n      console.error(\"Error fetching place details:\", error);\n      // eslint-disable-next-line no-undef\n      this.#onErrorCallback(error);\n    } finally {\n      // Reset the input and suggestions regardless of success/error\n      this._reset(true, data); // Refresh token after selection\n    }\n  }\n\n  /**\n   * Creates a new Google Places Autocomplete Session Token.\n   * This should be called before starting a new series of autocomplete requests.\n   */\n  _refreshToken() {\n    try {\n      // eslint-disable-next-line no-undef\n      this.#request.sessionToken =\n        new google.maps.places.AutocompleteSessionToken();\n    } catch (error) {\n      console.error(\"Error creating session token:\", error);\n      // eslint-disable-next-line no-undef\n      this.#onErrorCallback(error);\n    }\n  }\n\n  /**\n   * Sets the fields to be fetched for the selected place.\n   * This allows you to specify which details you want to retrieve\n   * when a place is selected, such as displayName, formattedAddress,\n   * addressComponents, etc.\n   * @param {Array<string>} fields - Array of field names to fetch.\n   */\n  setFetchFields(fields) {\n    this._setFetchFields(fields);\n  }\n  /**\n   * Gets the current fields that will be fetched for the selected place.\n   * This is useful for understanding what details will be available\n   * when a place is selected, such as displayName, formattedAddress,\n   * addressComponents, etc.\n   * @returns {Array<string>} The current fetch fields.\n   */\n  getFetchFields() {\n    return this.#fetchFields;\n  }\n\n  /**\n   * Sets the request parameters for the Places Autocomplete instance.\n   * This method allows you to update the request parameters dynamically,\n   * such as the input value, region codes, language, etc.\n   * It merges the provided parameters with the default request parameters,\n   * allowing you to override specific values while keeping the defaults intact.\n   * This is useful for updating the search criteria without needing to recreate the instance.\n   * @param {*} params\n   */\n  setRequestParams(params) {\n    if (\n      typeof params === \"object\" &&\n      !Array.isArray(params) &&\n      params !== null\n    ) {\n      // if params.input is provided, set it to the input element\n      if (params.input && typeof params.input === \"string\") {\n        this.#inputElement.value = params.input; // Set input value if provided\n      }\n      // Merge provided params with default request parameters\n      // This allows overriding specific request parameters while keeping defaults\n      this.#request = {\n        ...this.#defaultRequestParams,\n        ...params,\n      };\n    }\n  }\n\n  /**\n   * Returns the current request parameters used by the Places Autocomplete instance.\n   * This includes the input value, included region codes, language, and other settings.\n   * It is useful for debugging or when you need to know the current search criteria.\n   * @returns {Object} The current request parameters.\n   */\n  getRequestParams() {\n    return this.#request;\n  }\n\n  /**\n   * Sets the options for the Places Autocomplete instance.\n   * This method allows you to change the appearance and behavior of the autocomplete widget,\n   * such as classes, placeholder text, debounce time, etc.\n   * It merges the provided options with the default options, allowing you to override specific values\n   * while keeping the defaults intact.\n   * This is useful for updating the widget's configuration without needing to recreate the instance.\n   * @param {*} options\n   */\n  setOptions(options) {\n    if (\n      typeof options === \"object\" &&\n      !Array.isArray(options) &&\n      options !== null\n    ) {\n      this._detachEventListeners(); // Detach event listeners\n\n      // // Ensure classes are deeply merged if user provides partial classes\n      const tmpClasses = options.classes || {};\n      delete options.classes; // Remove classes from options to avoid overwriting\n\n      // Merge provided options with default options\n      // This allows overriding specific options while keeping defaults\n      this.#options = {\n        ...this.#defaultOptions,\n        ...options,\n      };\n\n      // Merge classes with defaults\n      // This allows overriding specific classes while keeping defaults\n      if (\n        tmpClasses &&\n        typeof tmpClasses === \"object\" &&\n        Object.keys(tmpClasses).length > 0\n      ) {\n        this.#options.classes = {\n          ...this.#defaultClasses,\n          ...tmpClasses,\n        };\n      } else {\n        this.#options.classes = { ...this.#defaultClasses }; // Use defaults if no classes provided\n      }\n\n      this._initialiseDebouncedRequest(); // Reinitialize the debounced request function\n      this._createPACStructure(); // Recreate the structure with new options\n      // Reattach the input event listener to the input element\n      // This is necessary to ensure the new input element is ready for events\n      if (this.#inputElement) {\n        this._attachedEventListeners();\n      } // Reinitialize the autocomplete functionality\n    }\n  }\n\n  /**\n   * Gets the current options used by the Places Autocomplete instance.\n   * @returns {Object} The current options.\n   */\n  getOptions() {\n    /**\n     * Returns the current options used by the Places Autocomplete instance.\n     * This includes classes, placeholder text, debounce time, and other settings.\n     * It is useful for debugging or when you need to know the current configuration of the widget.\n     * @returns {Object} The current options.\n     */\n    return this.#options;\n  }\n\n  /**\n   * Clears the autocomplete input field and suggestions list.\n   * This method resets the state of the autocomplete widget,\n   * allowing the user to start a new search without any previous input or suggestions.\n   * It also refreshes the Google Places session token to ensure a new session.\n   * @returns {void}\n   */\n  clear() {\n    this._reset(true);\n  }\n\n  /**\n   * Destroys the PacAutocomplete instance.\n   * Removes event listeners, clears the DOM, and nullifies properties.\n   * This method should be called when the autocomplete widget is no longer needed.\n   * It ensures that all resources are cleaned up to prevent memory leaks.\n   * @returns {void}\n   */\n  destroy() {\n    // Remove event listeners, remove elements from DOM\n    this._detachEventListeners(); // Detach event listeners\n    // Nullify properties\n    this.#containerId = null;\n    this.#pacEl = null;\n    this.#googleMapsApiKey = null;\n    this.#googleMapsApiVersion = null;\n    this.#options = null;\n    this.#request = null;\n    this.#inputElement = null;\n    this.#container = null;\n    this.#ul = null;\n    this.#kbdEscape = null;\n    this.#kbdUp = null;\n    this.#kbdDown = null;\n    this.#allSuggestions = null;\n    this.#currentSuggestion = -1;\n    this.#onDataCallback = null;\n    this.#onErrorCallback = null;\n    this.#debouncedMakeAcRequest = null;\n  }\n}\n"],"names":["PlacesAutocomplete","#containerId","#pacEl","#googleMapsApiKey","#googleMapsApiVersion","#options","#request","#inputElement","#container","#ul","#kbdEscape","#kbdUp","#kbdDown","#allSuggestions","#currentSuggestion","#onDataCallback","#onErrorCallback","#debouncedMakeAcRequest","#defaultOptions","#defaultClasses","#defaultRequestParams","#fetchFields","#defaultFetchFields","config","place","error","suggestions","suggestionElements","fields","func","wait","timeout","args","later","distance","units","value","unitLabel","g","h","a","k","p","c","l","q","m","b","d","r","e","u","f","n","t","section","iconContainer","icon","label","kbdContainer","refresh","placeData","li","cl","link","currentLi","currentButton","response","type","locality","adminArea","postalCode","country","index","button","className","divContainer","svgIcon","suggestion","liDivOne","liDivTwo","mapPinIcon","divPContainer","pOne","pTwo","pThree","predictionText","originalText","matches","lastIndex","outerSpan","innerSpan","match","remainingText","secondaryText","data","params","options","tmpClasses"],"mappings":"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"}