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}} +
+
+
+
+
Smart Answers
+ {{#shouldShowAnswerError}} +
{{{answerErrorMessage}}}
+ {{/shouldShowAnswerError}} +
+ {{{fullAnswerFormatted}}} + {{^showMoreButtonVisible}} + {{#answerLoading}} +
+ {{/answerLoading}} + {{/showMoreButtonVisible}} +
+ +
+ + {{#showMoreButtonVisible}} +
+ {{#answerLoading}} +
+ {{/answerLoading}} + +
+ {{/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: +` +
+
+ NEAR + + {{#shouldShowLocationDistanceDropdown}} + WITHIN + + {{/shouldShowLocationDistanceDropdown}} +
+
+ `, + 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: ` +
+
+
+
+ +
+
+
Filter By
+
+
+
+ +
+ `, + 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: ` +
+
+
+ {{label}} +
+
+
+
+
+ `, + facetListTitleContainerClass: `searchstax-facet-title-container`, + facetListContainerClass: `searchstax-facet-values-container`, + }, + clearFacetsTemplate: { + template: ` + {{#shouldShow}}} +
+
Clear Filters
+
+ {{/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: ` +
+
Filter By
+
+ `, + 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}} +
+ +
+ {{/searchExecuted}} + `, + }, + }, + renderMethod: renderConfig.resultsWidget.renderMethod, +}); + +searchstax.addPaginationWidget("searchstax-pagination-container", { + templates: { + mainTemplate: { + template: ` + {{#results.length}} +
+
+ < Previous +
+ {{startResultIndex}} - {{endResultIndex}} of {{totalResults}} +
+ Next > +
+
+ {{/results.length}} + `, + nextButtonClass: `searchstax-pagination-next`, + previousButtonClass: `searchstax-pagination-previous`, + }, + infiniteScrollTemplate: { + template: ` + {{#results.length}} + {{^isLastPage}} + Show More + {{/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}} + + {{/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: ` +
+
+ {{#url}} + + {{/url}} +
+ {{name}} +
+ {{#description}} +

+ {{description}} +

+ {{/description}} + {{#url}} +

+ {{url}} +

+ {{/url}} +
+ `, + }, + }, + } +); + +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"] +}