diff --git a/pages/js/.DS_Store b/pages/js/.DS_Store
index 29e7631f..916942b3 100644
Binary files a/pages/js/.DS_Store and b/pages/js/.DS_Store differ
diff --git a/pages/js/config.js b/pages/js/config.js
index 067420ab..a42771d5 100644
--- a/pages/js/config.js
+++ b/pages/js/config.js
@@ -1,64 +1,59 @@
const initConfig = {
- acceleratorSample: {
- language: "en",
- searchURL:
- "https://searchcloud-2-us-east-1.searchstax.com/29847/corpsiteuxsamples-1442/emselect",
- suggesterURL:
- "https://searchcloud-2-us-east-1.searchstax.com/29847/corpsiteuxsamples-1442_suggester/emsuggest",
- searchAuth: "b065448ad1484e205f4851f0ce89d128e704e2f4",
- trackApiKey: "DPAOKNB9c5chZZDwN1Il9dLUCLMGF1ggehy0dWewZwk",
- authType: "token",
- relatedSearchesURL:
- "https://app.searchstax.com/api/v1/1442/related-search/",
- relatedSearchesAPIKey: "fac98ad405cc50e0c0693331e8d2119de592f0e3",
- analyticsBaseUrl: "https://analytics-us.searchstax.com",
- questionURL: "https://search-ai-us.searchstax.com/api/v1/1442/answer/",
- model: "Default",
- appId: "1442",
- },
- acceleratorRelatedSearchSample: {
- relatedSearchesURL:
- "https://app.searchstax.com/api/v1/1442/related-search/",
- relatedSearchesAPIKey: "fac98ad405cc50e0c0693331e8d2119de592f0e3",
- model: "Default",
- },
- jobSearchSample: {
- language: "en",
- searchURL:
- "https://searchcloud-2-us-east-1.searchstax.com/29847/jobsearchuxsample-1447/emselect",
- suggesterURL:
- "https://searchcloud-2-us-east-1.searchstax.com/29847/jobsearchuxsample-1447_suggester/emsuggest",
- searchAuth: "9c77f740418413b6701fe7a98cc7c640987a9af1",
- trackApiKey: "Cnpe2IL7uXaztPzavTIuDAiAmfydOa12f0iAGE0hLlY",
- authType: "token",
- analyticsBaseUrl: "https://analytics-us.searchstax.com",
- model: "Default",
- },
- jobSearchRelatedSearchSample: {
- relatedSearchesURL:
- "https://app.searchstax.com/api/v1/1447/related-search/",
- relatedSearchesAPIKey: "3014f9d0276906ac17fff16c500e5ee75b20d73c",
- analyticsBaseUrl: "https://analytics-us.searchstax.com",
- model: "Default",
- },
- searchStandaloneSearchSample: {
- language: "en",
- searchURL:
- "",
- suggesterURL:
- "",
- searchAuth: "",
- trackApiKey: "DPAOKNB9c5chZZDwN1Il9dLUCLMGF1ggehy0dWewZwk",
- authType: "token",
- relatedSearchesURL:
- "https://app.searchstax.com/api/v1/1442/related-search/",
- relatedSearchesAPIKey: "fac98ad405cc50e0c0693331e8d2119de592f0e3",
- analyticsBaseUrl: "https://analytics-us.searchstax.com",
- questionURL: "https://search-ai-us.searchstax.com/api/v1/1442/answer/",
- model: "Default",
- appId: "1442",
- },
-};
+ acceleratorSample: {
+ language: "en",
+ searchURL:
+ "https://searchcloud-2-us-east-1.searchstax.com/29847/corpsiteuxsamples-1442/emselect",
+ suggesterURL:
+ "https://searchcloud-2-us-east-1.searchstax.com/29847/corpsiteuxsamples-1442_suggester/emsuggest",
+ searchAuth: "b065448ad1484e205f4851f0ce89d128e704e2f4",
+ trackApiKey: "DPAOKNB9c5chZZDwN1Il9dLUCLMGF1ggehy0dWewZwk",
+ authType: "token",
+ relatedSearchesURL:
+ "https://app.searchstax.com/api/v1/1442/related-search/",
+ relatedSearchesAPIKey: "fac98ad405cc50e0c0693331e8d2119de592f0e3",
+ analyticsBaseUrl: 'https://analytics-us.searchstax.com',
+ questionURL: "https://search-ai-us.searchstax.com/api/v1/1442/answer/",
+ },
+ acceleratorRelatedSearchSample: {
+ relatedSearchesURL:
+ "https://app.searchstax.com/api/v1/1442/related-search/",
+ relatedSearchesAPIKey: "fac98ad405cc50e0c0693331e8d2119de592f0e3",
+ },
+ jobSearchSample: {
+ language: "en",
+ searchURL:
+ "https://searchcloud-2-us-east-1.searchstax.com/29847/jobsearchuxsample-1447/emselect",
+ suggesterURL:
+ "https://searchcloud-2-us-east-1.searchstax.com/29847/jobsearchuxsample-1447_suggester/emsuggest",
+ searchAuth: "9c77f740418413b6701fe7a98cc7c640987a9af1",
+ trackApiKey: "Cnpe2IL7uXaztPzavTIuDAiAmfydOa12f0iAGE0hLlY",
+ authType: "token",
+ analyticsBaseUrl: 'https://analytics-us.searchstax.com'
+ },
+ jobSearchRelatedSearchSample: {
+ relatedSearchesURL:
+ "https://app.searchstax.com/api/v1/1447/related-search/",
+ relatedSearchesAPIKey: "3014f9d0276906ac17fff16c500e5ee75b20d73c",
+ analyticsBaseUrl: 'https://analytics-us.searchstax.com'
+ },
+ locationSearchSample: {
+ language: "en",
+ searchURL:
+ "https://searchcloud-2-us-east-1.searchstax.com/29847/fiveguyslocationsdemo-4848/emselect",
+ suggesterURL:
+ "https://searchcloud-2-us-east-1.searchstax.com/29847/fiveguyslocationsdemo-4848_suggester/emsuggest",
+ searchAuth: "1641be461786e5b311905176ea67bcb8416c442d",
+ trackApiKey: "qYQh2M9XAE3sfBX2uUbNQ6UPTRK9zEoxptZ9tkfluVU",
+ authType: "token",
+ relatedSearchesURL:
+ "https://app.searchstax.com/api/v1/4848/related-search/",
+ relatedSearchesAPIKey: "9f755acd4eb7ba0935c318e7cb08f43cd0751257",
+ analyticsBaseUrl: 'https://analytics-us.searchstax.com',
+ questionURL: "https://search-ai-us.searchstax.com/api/v1/4848/answer/",
+ geocodingAPIKey: "**__add_your_own_**",
+ googleMapsApiKey: "**__add_your_own_**",
+ }
+ };
const renderConfig = {
inputWidget: {},
diff --git a/pages/js/location-search/README.md b/pages/js/location-search/README.md
new file mode 100644
index 00000000..69062ebc
--- /dev/null
+++ b/pages/js/location-search/README.md
@@ -0,0 +1,46 @@
+## Location Search with Google Maps Integration
+
+### Setting up Google Maps Integration
+
+- Create a project on [Google API Console](https://console.cloud.google.com)
+- Enable `Maps JavaScript API` for map integration
+- Enable `Places API` for geocoding integration
+- Go to "Credentials" and create an API key
+- Add API key to `googleMapsApiKey` field in config.js
+
+### Usage
+
+- Run the commands:
+```shell
+$ npm install
+$ npm run dev
+```
+- Open [http://localhost:5173](http://localhost:5173)
+
+### Search Page Layout
+
+The search page layout and functionality is defined in `src/main.ts`. Map results are loaded into the `searchstax-location-map` element.
+
+### JavaScript Functions
+
+Google Maps are added to the page and configured using several functions:
+
+- `loadGoogleMaps` adds the Google Maps code to the search page
+- `clearMarkers` removes and resets map pins when search results change
+- `processMapResults` looks for location results in the search response and creates map pins for each location
+
+The `searchstax.dataLayer.$searchResults.subscribe` function is used to watch for updated search results. If a `location` field is found in the response `metadata` then the location results are sent to `processMapResults` to display the appropriate map pins.
+
+```JS
+searchstax.dataLayer.$searchResults.subscribe((results) => {
+ const metadata = searchstax.dataLayer.$searchResultsMetadata.getValue();
+ if (metadata?.location?.field) {
+ locationField = metadata.location.field;
+ processMapResults(results);
+ } else {
+ clearMarkers();
+ locationField = '';
+ }
+ return results;
+});
+```
\ No newline at end of file
diff --git a/pages/js/location-search/index.html b/pages/js/location-search/index.html
new file mode 100644
index 00000000..635fdcc4
--- /dev/null
+++ b/pages/js/location-search/index.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+ Searchstax Seach Page
+
+
+
+
+
+
+
+
+
+ Searchstax Search Page
+
+
+
+
+
diff --git a/pages/js/location-search/main.scss b/pages/js/location-search/main.scss
new file mode 100644
index 00000000..ddb648d3
--- /dev/null
+++ b/pages/js/location-search/main.scss
@@ -0,0 +1,209 @@
+@import url("https://fonts.googleapis.com/css2?family=Barlow:wght@100;300;600&display=swap");
+@import "./node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/mainTheme.css";
+
+*,
+html {
+ font-family: "Barlow", sans-serif !important;
+}
+
+.container {
+ width: 100%;
+ margin: 20px auto;
+}
+
+h3 {
+ font-size: 22px;
+ font-weight: 400;
+}
+
+.searchstax {
+ &-search {
+ &-input-container {
+ width: 50%;
+ margin: auto;
+ font-size: 22px;
+ font-weight: 300;
+ box-shadow: 2px 2px 1px rgba(0, 0, 0, 0.35) !important;
+ border-radius: 6px !important;
+ &.searchstax-alternative-render {
+ width: 65%;
+ }
+ }
+ &-result {
+ display: flex;
+ flex-direction: row;
+ border-radius: 8px;
+ overflow: hidden;
+ padding: 0 !important;
+ height: 180px;
+
+ &-image-container {
+ width: 250px !important;
+ height: 100% !important;
+ overflow: unset !important;
+ }
+
+ &-content {
+ flex: 2;
+ padding: 1rem;
+ }
+
+ &-ribbon {
+ all: unset !important;
+ font-size: 12px !important;
+ }
+
+ &-title-wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ }
+
+ &-title {
+ font-size: 14px !important;
+ line-height: 1.2rem;
+ text-transform: uppercase;
+ }
+
+ &:hover {
+ cursor: pointer;
+ box-shadow: 1px 1px 5px 3px rgba(0, 0, 0, 0.25);
+ }
+ }
+ }
+
+ &-autosuggest-container {
+ margin-top: 55px !important;
+ border-radius: 0px 0px 10px 10px;
+ }
+
+ &-result {
+ &-image {
+ display: block;
+ height: 100% !important;
+ object-fit: cover;
+ }
+ }
+
+ &-facet {
+ &-title {
+ font-weight: 600;
+ margin-bottom: 10px;
+ }
+ &-value {
+ &-container {
+ margin-bottom: 5px;
+ }
+ &-label {
+ text-transform: capitalize !important;
+ }
+ &-count {
+ font-weight: 600 !important;
+ }
+ }
+ }
+
+ &-related-searches {
+ padding-left: 10px;
+ }
+
+ &-related-searches-container {
+ margin-top: 40px;
+ display: flex;
+ }
+
+ &-related-search {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ &-item {
+ display: block;
+ color: #111;
+ font-weight: 500;
+ &:hover {
+ color: #eb3500;
+ }
+ }
+ }
+}
+
+#searchstax-location-map {
+ min-height: 300px;
+}
+
+.search-details-container {
+ margin-top: 40px !important;
+}
+.feedback-container {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 20px;
+}
+
+.pill {
+ background: #ddd;
+ padding: 0.3rem;
+ text-transform: uppercase;
+ font-size: 10px;
+ font-weight: 600;
+ margin-right: 10px;
+ white-space: nowrap;
+}
+
+@media (min-width: 37.5em) {
+ .container {
+ width: calc(37.5em + ((100% - 37.5em) * 0.7));
+ }
+}
+.searchstax-main-page-title {
+ display: none;
+}
+
+#searchstax-feedback-container {
+ width: calc(50% - 5px) !important;
+ margin-left: auto !important;
+ margin-right: auto !important;
+ margin-bottom: 5px;
+}
+
+#feedbackWidgetContainer {
+ margin-top: 20px;
+}
+
+@media (max-width: 1024px) {
+ .searchstax-alternative-render {
+ width: 100% !important;
+ }
+ .searchstax-page-layout-container {
+ width: 100% !important;
+ margin-left: 0;
+ }
+ .searchstax-alternative-render {
+ width: 100% !important;
+ padding-left: 0;
+ }
+}
+
+@media (max-width: 550px) {
+ .searchstax-alternative-render {
+ .searchstax-location-input-container {
+ width: 100% !important;
+ }
+ display: block !important;
+ .searchstax-location-input-wrapper {
+ display: block !important;
+ .searchstax-location-input-label {
+ display: block;
+ }
+ input,
+ select {
+ width: 100% !important;
+ padding: 0;
+ padding-left: 10px;
+ }
+ select {
+ width: calc(100% + 10px) !important;
+ padding-right: 10px;
+ }
+ }
+ }
+}
diff --git a/pages/js/location-search/package.json b/pages/js/location-search/package.json
new file mode 100644
index 00000000..5cf60c8f
--- /dev/null
+++ b/pages/js/location-search/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "vite-test",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "sass-embedded": "^1.86.2",
+ "typescript": "~5.7.2",
+ "vite": "^6.2.0"
+ },
+ "dependencies": {
+ "@searchstax-inc/searchstudio-ux-js": "npm:@searchstax-inc/searchstudio-ux-js@^1.1.12",
+ "dompurify": "^3.2.5"
+ }
+}
diff --git a/pages/js/location-search/public/vite.svg b/pages/js/location-search/public/vite.svg
new file mode 100644
index 00000000..e7b8dfb1
--- /dev/null
+++ b/pages/js/location-search/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pages/js/location-search/src/counter.ts b/pages/js/location-search/src/counter.ts
new file mode 100644
index 00000000..09e5afd2
--- /dev/null
+++ b/pages/js/location-search/src/counter.ts
@@ -0,0 +1,9 @@
+export function setupCounter(element: HTMLButtonElement) {
+ let counter = 0
+ const setCounter = (count: number) => {
+ counter = count
+ element.innerHTML = `count is ${counter}`
+ }
+ element.addEventListener('click', () => setCounter(counter + 1))
+ setCounter(0)
+}
diff --git a/pages/js/location-search/src/main.ts b/pages/js/location-search/src/main.ts
new file mode 100644
index 00000000..8a51da11
--- /dev/null
+++ b/pages/js/location-search/src/main.ts
@@ -0,0 +1,660 @@
+import { Searchstax } from "@searchstax-inc/searchstudio-ux-js";
+// @ts-ignore
+import { initConfig, renderConfig } from "../../config.js";
+
+const searchstax = new Searchstax();
+var locationField = "";
+
+function makeId(length: number) {
+ let result = "";
+ const characters =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ const charactersLength = characters.length;
+ for (let i = 0; i < length; i++) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ }
+ return result;
+}
+
+function searchstaxEmailOverride() {
+ return "";
+}
+
+function loadGoogleMaps() {
+ return new Promise((resolve, reject) => {
+ if (window.google && window.google.maps) {
+ resolve();
+ return;
+ }
+ const script = document.createElement("script");
+ script.src = `https://maps.googleapis.com/maps/api/js?key=${initConfig.locationSearchSample.googleMapsApiKey}`;
+ script.async = true;
+ script.defer = true;
+ script.onload = () => resolve();
+ script.onerror = (e) => reject(e);
+ document.head.appendChild(script);
+ });
+}
+
+let map;
+let markers = [];
+
+function clearMarkers() {
+ markers.forEach(marker => marker.setMap(null));
+ markers = [];
+}
+
+async function processMapResults(results) {
+ clearMarkers();
+ if (!locationField) return;
+
+ const locations = results.reduce((acc, item) => {
+ const locField = item.allFields.find(field => field.key === locationField);
+ if (locField && locField.value) {
+ const [lat, lng] = locField.value.split(",").map(Number);
+ acc.push({
+ lat,
+ lng,
+ title: item.title,
+ description: item.description,
+ distance: item.distance,
+ unit: item.unit
+ });
+ }
+ return acc;
+ }, []);
+
+ let mapContainer = document.getElementById("searchstax-location-map");
+
+ if (locations.length === 0) {
+ mapContainer.style.display = "none";
+ return;
+ }
+
+ mapContainer.style.display = "block";
+
+ try {
+ await loadGoogleMaps();
+ }
+ catch (error) {
+ console.error("Failed to load Google Maps API", error);
+ return;
+ }
+
+ if (!mapContainer) {
+ mapContainer = document.createElement("div");
+ mapContainer.id = "map";
+ document.body.appendChild(mapContainer);
+ }
+
+ map = new google.maps.Map(mapContainer, {
+ zoom: 10,
+ center: { lat: locations[0].lat, lng: locations[0].lng },
+ });
+
+ locations.forEach(({ lat, lng, title, description, distance, unit}) => {
+ const marker = new google.maps.Marker({
+ position: { lat, lng },
+ map: map,
+ title: title,
+ });
+ const popup = new google.maps.InfoWindow({
+ content: `
+
+
${title}
+
${description || ""}
+
${distance} ${unit} away
+
+ `,
+ });
+
+ marker.addListener("click", () => {
+ popup.open(map, marker);
+ });
+ markers.push(marker);
+ });
+}
+
+searchstax.initialize({
+ ...initConfig.locationSearchSample,
+ sessionId: makeId(25),
+ hooks: {
+ beforeSearch: (props) => {
+ const propsCopy = { ...props };
+ return propsCopy;
+ },
+ afterSearch: (results) => {
+ const copy = [...results];
+ return copy;
+ },
+ },
+});
+searchstax.dataLayer.$searchResults.subscribe((results) => {
+ const metadata = searchstax.dataLayer.$searchResultsMetadata.getValue();
+ if (metadata?.location?.field) {
+ locationField = metadata.location.field;
+ processMapResults(results);
+ } else {
+ clearMarkers();
+ locationField = '';
+ }
+ return results;
+});
+searchstax.addAnswerWidget("searchstax-answer-container", {
+ showShowMoreAfterWordCount: 100,
+ templates: {
+ main: {
+ template: `
+ {{#shouldShowAnswer}}
+
+
+
+
+
+ {{#showMoreButtonVisible}}
+
+ {{/showMoreButtonVisible}}
+
+
+
+ {{/shouldShowAnswer}}
+ `,
+ },
+ },
+ feedbackwidget: {
+ renderFeedbackWidget: true,
+ emailOverride: searchstaxEmailOverride,
+ thumbsUpValue: 10,
+ thumbsDownValue: 0,
+ lightweightTemplateOverride: `
+
+ `,
+ },
+});
+searchstax.addSearchFeedbackWidget("search-feedback-container", {
+ templates: {
+ main: {
+ template: `
+ {{#searchExecuted}}
+ Skip to results section
+
+
+ {{#hasResults}}
+ Showing {{startResultIndex}} - {{endResultIndex}} of {{totalResults}} results {{#searchTerm}} for "{{searchTerm}}" {{/searchTerm}}
+
+ {{#autoCorrectedQuery}}
+ Search instead for
{{originalQuery}}
+ {{/autoCorrectedQuery}}
+
+ {{/hasResults}}
+
+ {{/searchExecuted}}
+ `,
+ originalQueryClass: `searchstax-feedback-original-query`,
+ },
+ },
+});
+
+searchstax.addSearchInputWidget("searchstax-input-container", {
+ suggestAfterMinChars: renderConfig.inputWidget.suggestAfterMinChars,
+ templates: {
+ mainTemplate: {
+ template: `
+
+ `,
+ searchInputId: "searchstax-search-input",
+ },
+ autosuggestItemTemplate: {
+ template: `
+ {{{term}}}
+ `,
+ },
+ },
+});
+
+searchstax.addSearchLocationWidget("searchstax-location-container", {
+ templates:{
+ mainTemplate: {
+ template:
+`
+
+ `,
+ locationInputId: "searchstax-location-input",
+ radiusInputId: "searchstax-location-radius-select"
+ },
+ },
+ hooks: {
+ locationDecode: (term) => {
+ return new Promise((resolve) => {
+ // make a request to google geocoding API to retrieve lat, lon and address
+
+ const geocodingURL = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
+ term
+ )}&key=${initConfig.locationSearchSample.geocodingAPIKey}`;
+ fetch(geocodingURL)
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.status === "OK" && data.results.length > 0) {
+ const result = data.results[0];
+ const location = {
+ lat: result.geometry.location.lat,
+ lon: result.geometry.location.lng,
+ address: result.formatted_address,
+ };
+ resolve(location);
+ } else {
+ resolve({
+ address: undefined,
+ lat: undefined,
+ lon: undefined,
+ error: true
+ });
+ }
+ })
+ .catch(() => {
+ resolve({
+ address: undefined,
+ lat: undefined,
+ lon: undefined,
+ error: true
+ });
+ });
+ });
+ },
+ },
+});
+
+searchstax.addFacetsWidget("searchstax-facets-container", {
+ facetingType: renderConfig.facetsWidget.facetingType,
+ itemsPerPageDesktop: renderConfig.facetsWidget.itemsPerPageDesktop,
+ itemsPerPageMobile: renderConfig.facetsWidget.itemsPerPageMobile,
+ templates: {
+ mainTemplateDesktop: {
+ template: `
+ {{#hasResultsOrExternalPromotions}}
+
+ {{/hasResultsOrExternalPromotions}}
+ `,
+ facetsContainerClass: `searchstax-facets-container-desktop`,
+ selectedFacetsContainerClass: `searchstax-facets-pills-selected`,
+ },
+ mainTemplateMobile: {
+ template: `
+
+
+
+ `,
+ facetsContainerClass: `searchstax-facets-container-mobile`,
+ closeOverlayTriggerClasses: [
+ "searchstax-facets-mobile-overlay-done",
+ "searchstax-search-close",
+ ],
+ filterByContainerClass: `searchstax-facets-pills-container`,
+ selectedFacetsContainerClass: `searchstax-facets-pills-selected`,
+ },
+ showMoreButtonContainerTemplate: {
+ template: `
+
+ {{#showingAllFacets}}
+
less
+ {{/showingAllFacets}}
+ {{^showingAllFacets}}
+
more {{onShowMoreLessClick}}
+ {{/showingAllFacets}}
+
+ `,
+ showMoreButtonClass: `searchstax-facet-show-more-container`,
+ },
+ facetItemContainerTemplate: {
+ template: `
+
+ `,
+ facetListTitleContainerClass: `searchstax-facet-title-container`,
+ facetListContainerClass: `searchstax-facet-values-container`,
+ },
+ clearFacetsTemplate: {
+ template: `
+ {{#shouldShow}}}
+
+ {{/shouldShow}}
+ `,
+ containerClass: `searchstax-facets-pill-clear-all`,
+ },
+ facetItemTemplate: {
+ template: `
+
+
+
+ {{value}}
+ ({{count}})
+ `,
+ inputCheckboxClass: `searchstax-facet-input-checkbox`,
+ checkTriggerClasses: [
+ "searchstax-facet-value-label",
+ "searchstax-facet-value-count",
+ ],
+ },
+ filterByTemplate: {
+ template: `
+
+ `,
+ containerClass: `searchstax-facets-pill-filter-by`,
+ },
+ selectedFacetsTemplate: {
+ template: `
+
+
{{value}} ({{count}})
+
+
+ `,
+ containerClass: `searchstax-facets-pill-facets`,
+ },
+ },
+});
+
+searchstax.addSearchSortingWidget("search-sorting-container", {
+ templates: {
+ main: {
+ template: `
+ {{#searchExecuted}}
+ {{#hasResultsOrExternalPromotions}}
+
+
+
+
+ {{/hasResultsOrExternalPromotions}}
+ {{/searchExecuted}}
+ `,
+ selectId: "searchstax-search-order-select",
+ },
+ },
+});
+
+searchstax.addSearchResultsWidget("searchstax-results-container", {
+ templates: {
+ mainTemplate: {
+ template: `
+
+ `,
+ searchResultsContainerId: `searchstax-search-results`,
+ },
+ searchResultTemplate: {
+ template: `
+
+
+ {{#promoted}}
+
+ {{/promoted}}
+
+ {{#ribbon}}
+
+ {{{ribbon}}}
+
+ {{/ribbon}}
+
+ {{#thumbnail}}
+

+ {{/thumbnail}}
+
+
{{{title}}}
+
+
+ {{#paths}}
+
+ {{{paths}}}
+
+ {{/paths}}
+
+ {{#description}}
+
+ {{{description}}}
+
+ {{/description}}
+
+ {{#unmappedFields}}
+ {{#isImage}}
+
+

+
+ {{/isImage}}
+ {{^isImage}}
+
+ {{{value}}}
+
+ {{/isImage}}
+ {{/unmappedFields}}
+ {{#distance}}
+
+ {{distance}} {{unit}}
+
+ {{/distance}}
+
+
+ `,
+ searchResultUniqueIdAttribute: `data-searchstax-unique-result-id`,
+ },
+ noSearchResultTemplate: {
+ template: `
+ {{#searchExecuted}}
+
+
+ Showing
no results for
"{{ searchTerm }}"
+
+ {{#spellingSuggestion}}
+
Did you mean {{ spellingSuggestion }}?
+ {{/spellingSuggestion}}
+
+
+ - Try searching for search related terms or topics. We offer a wide variety of content to help you get the information you need.
+ - Lost? Click on the ‘X” in the Search Box to reset your search.
+
+
+ {{/searchExecuted}}
+ `,
+ },
+ },
+ renderMethod: renderConfig.resultsWidget.renderMethod,
+});
+
+searchstax.addPaginationWidget("searchstax-pagination-container", {
+ templates: {
+ mainTemplate: {
+ template: `
+ {{#results.length}}
+
+ {{/results.length}}
+ `,
+ nextButtonClass: `searchstax-pagination-next`,
+ previousButtonClass: `searchstax-pagination-previous`,
+ },
+ infiniteScrollTemplate: {
+ template: `
+ {{#results.length}}
+ {{^isLastPage}}
+
+ {{/isLastPage}}
+ {{/results.length}}
+ `,
+ loadMoreButtonClass: `searchstax-pagination-load-more`,
+ },
+ },
+});
+
+searchstax.addRelatedSearchesWidget("searchstax-related-searches-container", {
+ relatedSearchesURL:
+ initConfig.locationSearchSample.relatedSearchesURL,
+ relatedSearchesAPIKey:
+ initConfig.locationSearchSample.relatedSearchesAPIKey,
+ templates: {
+ main: {
+ template: `
+ {{#hasRelatedSearches}}
+
+ Related searches:
+ {{#relatedSearches}}
+
+ {{/relatedSearches}}
+
+ {{/hasRelatedSearches}}
+ `,
+ relatedSearchesContainerClass: "searchstax-related-search",
+ },
+ relatedSearch: {
+ template: `
+
+ {{ related_search }}{{^last}},{{/last}}
+
+ `,
+ relatedSearchContainerClass: "searchstax-related-search-item",
+ },
+ },
+});
+
+searchstax.addExternalPromotionsWidget(
+ "searchstax-external-promotions-layout-container",
+ {
+ templates: {
+ mainTemplate: {
+ template: `
+ {{#hasExternalPromotions}}
+
+ External promotions go here
+
+ {{/hasExternalPromotions}}
+ `,
+ externalPromotionsContainerId:
+ "searchstax-external-promotions-container",
+ },
+ externalPromotion: {
+ template: `
+
+ `,
+ },
+ },
+ }
+);
+
+document.addEventListener("click", function (event) {
+ // @ts-ignore
+ if (event.target.classList.contains("searchstax-related-search-item")) {
+ scrollToTop();
+ }
+});
+
+function scrollToTop() {
+ window.scrollTo({
+ top: 0,
+ behavior: "smooth",
+ });
+}
diff --git a/pages/js/location-search/src/style.css b/pages/js/location-search/src/style.css
new file mode 100644
index 00000000..7f5d2ee2
--- /dev/null
+++ b/pages/js/location-search/src/style.css
@@ -0,0 +1,108 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #3178c6aa);
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
+
+#searchstax-feedback-container{
+ width: calc(50% + 40px);
+ margin-left: auto;
+ margin-right: auto;
+}
+
+@media (max-width: 767px) {
+ #searchstax-feedback-container{
+ width: calc(100% + 40px);
+ }
+}
diff --git a/pages/js/location-search/src/typescript.svg b/pages/js/location-search/src/typescript.svg
new file mode 100644
index 00000000..d91c910c
--- /dev/null
+++ b/pages/js/location-search/src/typescript.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pages/js/location-search/src/vite-env.d.ts b/pages/js/location-search/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/pages/js/location-search/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/pages/js/location-search/tsconfig.json b/pages/js/location-search/tsconfig.json
new file mode 100644
index 00000000..a4883f28
--- /dev/null
+++ b/pages/js/location-search/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}