1.0.2 • Published 9 months ago

@searchstax-inc/searchstudio-ux-vue v1.0.2

Weekly downloads
-
License
-
Repository
-
Last release
9 months ago

sitesearch-ux-vue

Library to build Site Search page

changelog

changelog is documented at CHANGELOG.md

Installation

npm install following package npm install --save @searchstax-inc/searchstudio-ux-vue

Add following code to <head>

<script type="text/javascript">
      var _msq = _msq || []; //declare object
      var analyticsBaseUrl = 'https://analytics-us-east.searchstax.co';
      (function () {
        var ms = document.createElement('script');
        ms.type = 'text/javascript';
        ms.src = 'https://static.searchstax.co/studio-js/v3/js/studio-analytics.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(ms, s);
      })();
    </script>

Usage

After importing SearchstaxWrapper component needs to wrap all other components:

<SearchstaxWrapper
    :language="sampleConfig.language"
    :searchURL="sampleConfig.searchURL"
    :suggesterURL="sampleConfig.suggesterURL"
    :trackApiKey="sampleConfig.trackApiKey"
    :searchAuth="sampleConfig.searchAuth"
    :authType="sampleConfig.authType"
    :router="sampleConfig.router"
    :beforeSearch="sampleConfig.hooks.beforeSearch" // callback function to intercept search object before search is fired
    :afterSearch="sampleConfig.hooks.afterSearch" //  callback function to handle results after search is fired

  >
  // other components will go there

  </SearchstaxWrapper>

Initialization

Initialization object needs to be of type: ISearchstaxConfig

Initialization example

sampleConfig = {
    language: "en",
    searchURL: "",
    suggesterURL: "",
    trackApiKey: "",
    searchAuth: "",
    authType: "basic",
    router: {
      enabled: true,
      routeName: "searchstax",
      title: (result: ISearchObject) => {
        return "Search results for: " + result.query;
      },
      ignoredKeys: [],
    },
    hooks: {
      beforeSearch: function (props: ISearchObject) {
        const propsCopy = { ...props };
        return propsCopy;
      },
      afterSearch: function (results: ISearchstaxParsedResult[]) {
        const copy = [...results];
        return copy;
      },
    }
  };

Initial layout

Our base theme is designed with this layout in mind but it is optional as all widgets have id parameters and can be attached to any element.

<SearchstaxWrapper
    :language="sampleConfig.language"
    :searchURL="sampleConfig.searchURL"
    :suggesterURL="sampleConfig.suggesterURL"
    :trackApiKey="sampleConfig.trackApiKey"
    :searchAuth="sampleConfig.searchAuth"
    :authType="sampleConfig.authType"
    :router="sampleConfig.router"
    :beforeSearch="sampleConfig.hooks.beforeSearch" // callback function to intercept search object before search is fired
    :afterSearch="sampleConfig.hooks.afterSearch" //  callback function to handle results after search is fired
  >
    <template #default>
      <div class="searchstax-page-layout-container">
        <SearchstaxInputWidget
          :afterAutosuggest="afterAutosuggest"
          :beforeAutosuggest="beforeAutosuggest"
          :suggestAfterMinChars="3"
        >
        </SearchstaxInputWidget>
        <div class="search-details-container">
          <SearchstaxSearchFeedbackWidget></SearchstaxSearchFeedbackWidget>
          <SearchstaxSortingWidget></SearchstaxSortingWidget>
        </div>
        <div class="searchstax-page-layout-facet-result-container">
          <div class="searchstax-page-layout-facet-container">
            <SearchstaxFacetsWidget
              :facetingType="'or'"
              :itemsPerPageDesktop="3"
              :itemsPerPageMobile="99"
            >
            </SearchstaxFacetsWidget>
          </div>
          <div class="searchstax-page-layout-result-container">
            <div id="searchstax-external-promotions-layout-container"></div>
            <SearchstaxResultWidget :afterLinkClick="afterLinkClick">
            </SearchstaxResultWidget>
            <div id="searchstax-related-searches-container"></div>
            <SearchstaxPaginationWidget>
            </SearchstaxPaginationWidget>
          </div>
        </div>
      </div>
    </template>
  </SearchstaxWrapper>

widgets

Following widgets are available:

Input Widget

Result Widget

Facets Widget

Pagination Widget

SearchFeedback Widget

RelatedSearches Widget

ExternalPromotions Widget

sorting Widget

Input Widget

example of input widget initialization with minimum options

<SearchstaxInputWidget
          :afterAutosuggest="afterAutosuggest"
          :beforeAutosuggest="beforeAutosuggest"
          :suggestAfterMinChars="3"
        ></SearchstaxInputWidget>

example of input widget initialization with template override

<SearchstaxInputWidget
          :afterAutosuggest="afterAutosuggest"
          :beforeAutosuggest="beforeAutosuggest"
          :suggestAfterMinChars="3"
        >
          <template #input="{ suggestions, onMouseLeave, onMouseOver, onMouseClick }">
            <div class="searchstax-search-input-wrapper">
              <input
                type="text"
                id="searchstax-search-input"
                class="searchstax-search-input"
                placeholder="SEARCH FOR..."
              />
              <div
                class="searchstax-autosuggest-container"
                :class="{ 'hidden': suggestions.length === 0 }"
                @mouseleave="onMouseLeave"
              >
                <div
                  class="searchstax-autosuggest-item"
                  v-for="suggestion in suggestions"
                  :key="suggestion.term"
                >
                  <div
                    class="searchstax-autosuggest-item-term-container"
                    v-html="suggestion.term"
                    @mouseover="onMouseOver(suggestion)"
                    @click.stop="onMouseClick()"
                  ></div>
                </div>
              </div>
              <button
                class="searchstax-spinner-icon"
                id="searchstax-search-input-action-button"
              >
              </button>
            </div>
          </template>
        </SearchstaxInputWidget>

Result Widget

example of results widget initialization with minimum options

<SearchstaxResultWidget :afterLinkClick="afterLinkClick"></SearchstaxResultWidget>

example of results widget initialization with infinite scroll behavior

<SearchstaxResultWidget :afterLinkClick="afterLinkClick" :renderMethod="'infiniteScroll'"></SearchstaxResultWidget>

example of result widget initialization with template override

 <SearchstaxResultWidget :afterLinkClick="afterLinkClick">
              <template #results="{ searchResults, resultClicked }">
                <div
                  class="searchstax-search-results"
                  v-if="searchResults && searchResults.length"
                >
                  <div
                    class="searchstax-search-result"
                    :class="{ 'has-thumbnail': searchResult.thumbnail }"
                    :key="searchResult.uniqueId"
                    v-for="searchResult in searchResults"
                  >
                    <div
                      v-if="searchResult.promoted"
                      class="searchstax-search-result-promoted"
                    ></div>
                    <a
                      v-if="searchResult.url"
                      :href="searchResult.url"
                      :data-searchstax-unique-result-id="searchResult.uniqueId"
                      @click="resultClicked(searchResult, $event)"
                      class="searchstax-result-item-link"
                    ></a>
                    <div
                      v-if="searchResult.ribbon"
                      class="searchstax-search-result-ribbon"
                    > {{ searchResult.ribbon }} </div>
                    <img
                      v-if="searchResult.thumbnail"
                      :src="searchResult.thumbnail"
                      class="searchstax-thumbnail"
                    />
                    <div class="searchstax-search-result-title-container">
                      <span class="searchstax-search-result-title">{{ searchResult.title }}</span>
                    </div>
                    <p
                      v-if="searchResult.paths"
                      class="searchstax-search-result-common"
                    > {{ searchResult.paths }} </p>
                    <p
                      v-if="searchResult.description"
                      class="searchstax-search-result-description searchstax-search-result-common"
                    > {{ searchResult.description }} </p>
                    <div
                      :key="unmappedField.key"
                      v-for="unmappedField in searchResult.unmappedFields"
                    >
                      <div
                        v-if="unmappedField.isImage && typeof unmappedField.value === 'string'"
                        class="searchstax-search-result-image-container"
                      >
                        <img
                          :src="unmappedField.value"
                          class="searchstax-result-image"
                        />
                      </div>
                      <div v-else>
                        <p class="searchstax-search-result-common"> {{ unmappedField.value }} </p>
                      </div>
                    </div>
                  </div>
                </div>
              </template>
            </SearchstaxResultWidget>

Pagination Widget

example of pagination widget initialization with minimum options

<SearchstaxPaginationWidget></SearchstaxPaginationWidget>

example of pagination widget initialization with various options

 <template #pagination="{ paginationData, previousPage, nextPage }">
                <div
                  class="searchstax-pagination-container"
                  v-if="paginationData"
                >
                  <div class="searchstax-pagination-content">
                    <a
                      class="searchstax-pagination-previous"
                      :disabled="paginationData?.isFirstPage"
                      @click="previousPage"
                      id="searchstax-pagination-previous"
                    > &lt; Previous </a>
                    <div class="searchstax-pagination-details"> {{ paginationData?.startResultIndex }} - {{
                      paginationData?.endResultIndex }} of {{ paginationData?.totalResults }} </div>
                    <a
                      class="searchstax-pagination-next"
                      :disabled="paginationData?.isLastPage"
                      @click="nextPage"
                      id="searchstax-pagination-next"
                    >Next ></a>
                  </div>
                </div>
              </template>

example of pagination widget initialization with infinite scroll template override

<template #infiniteScroll="{ nextPage }">
<div className="searchstax-pagination-container"
                <a
                  class="searchstax-pagination-load-more"
                  @click="nextPage"
                >Load More override</a>
</div>
</template>

Facets Widget

example of facets widget initialization with minimum options

<SearchstaxFacetsWidget
              :facetingType="'or'"
              :itemsPerPageDesktop="3"
              :itemsPerPageMobile="99"
            ></SearchstaxFacetsWidget>

example of facets widget initialization with template overrides

             <SearchstaxFacetsWidget
              :facetingType="'or'"
              :itemsPerPageDesktop="3"
              :itemsPerPageMobile="99"
            >
              <template
                #desktopFacets="{ facetsTemplateDataDesktop, isNotDeactivated, toggleFacetGroup, isChecked, selectFacet, showMoreLessDesktop, facetContainers }"
              >
                <div
                  v-if="facetsTemplateDataDesktop?.hasResultsOrExternalPromotions"
                  class="searchstax-facets-container-desktop"
                >
                  <div
                    v-for="facet in facetsTemplateDataDesktop.facets"
                    :key="facet"
                    class="searchstax-facet-container"
                    :class="{ 'active': isNotDeactivated(facet.name) }"
                  >
                    <div>
                      <div
                        class="searchstax-facet-title-container"
                        @click="toggleFacetGroup(facet.name)"
                      >
                        <div class="searchstax-facet-title"> {{ facet.label }} </div>
                        <div class="searchstax-facet-title-arrow active"></div>
                      </div>
                      <div class="searchstax-facet-values-container">
                        <div
                          v-for="(facetValue, key) in facet.values"
                          :key="facetValue.value + facetValue.parentName"
                          class="searchstax-facet-value-container"
                          :class="{ 'searchstax-facet-value-disabled': facetValue.disabled }"
                          :ref="el => { facetContainers[key + facet.name] = el }"
                        >
                          <div class="searchstax-facet-input">
                            <input
                              type="checkbox"
                              class="searchstax-facet-input-checkbox"
                              :checked="isChecked(facetValue)"
                              :disabled="facetValue.disabled"
                              @click="selectFacet(key + facet.name, $event, facetValue, true)"
                            />
                          </div>
                          <div
                            class="searchstax-facet-value-label"
                            @click="selectFacet(key + facet.name, $event, facetValue, false)"
                          >{{ facetValue.value }}</div>
                          <div
                            class="searchstax-facet-value-count"
                            @click="selectFacet(key + facet.name, $event, facetValue, false)"
                          >({{ facetValue.count }})</div>
                        </div>
                        <div
                          class="searchstax-facet-show-more-container"
                          v-if="facet.hasMoreFacets"
                        >
                          <div
                            class="searchstax-facet-show-more-container"
                            @click="showMoreLessDesktop($event, facet)"
                          >
                            <div
                              v-if="facet.showingAllFacets"
                              class="searchstax-facet-show-less-button searchstax-facet-show-button"
                            >less</div>
                            <div
                              v-else
                              class="searchstax-facet-show-more-button  searchstax-facet-show-button"
                            >more</div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </template>
              <template
                #mobileFacets="{ facetsTemplateDataMobile, selectedFacetsCheckboxes, isNotDeactivated, toggleFacetGroup, isChecked, selectFacet, showMoreLessDesktop, facetContainers, openOverlay, unselectFacet, unselectAll, closeOverlay }"
              >
                <div
                  class="searchstax-facets-container-mobile"
                  v-if="facetsTemplateDataMobile?.hasResultsOrExternalPromotions"
                >
                  <div class="searchstax-facets-pills-container">
                    <div
                      class="searchstax-facets-pill searchstax-facets-pill-filter-by"
                      @click="openOverlay"
                    >
                      <div class="searchstax-facets-pill-label">Filter By</div>
                    </div>
                    <div class="searchstax-facets-pills-selected">
                      <div
                        class="searchstax-facets-pill searchstax-facets-pill-facets"
                        v-for="facet in selectedFacetsCheckboxes"
                        :key="facet.value"
                        @click="unselectFacet(facet)"
                      >
                        <div class="searchstax-facets-pill-label">{{ facet.value }} ({{ facet.count }})</div>
                        <div class="searchstax-facets-pill-icon-close"></div>
                      </div>
                    </div>
                    <div
                      class="searchstax-facets-pill searchstax-clear-filters searchstax-facets-pill-clear-all"
                      v-if="selectedFacetsCheckboxes.length"
                      @click="unselectAll"
                    >
                      <div class="searchstax-facets-pill-label">Clear Filters</div>
                    </div>
                  </div>
                  <div
                    class="searchstax-facets-mobile-overlay"
                    :class="{ 'searchstax-show': facetsTemplateDataMobile?.overlayOpened }"
                  >
                    <div class="searchstax-facets-mobile-overlay-header">
                      <div class="searchstax-facets-mobile-overlay-header-title">Filter By</div>
                      <div
                        class="searchstax-search-close"
                        @click="closeOverlay"
                      ></div>
                    </div>
                    <div class="searchstax-facets-container-mobile">
                      <div
                        v-for="facet in facetsTemplateDataMobile?.facets"
                        :key="facet"
                        class="searchstax-facet-container"
                        :class="{ 'active': isNotDeactivated(facet.name) }"
                      >
                        <div>
                          <div
                            class="searchstax-facet-title-container"
                            @click="toggleFacetGroup(facet.name)"
                          >
                            <div class="searchstax-facet-title"> {{ facet.label }} </div>
                            <div class="searchstax-facet-title-arrow active"></div>
                          </div>
                          <div class="searchstax-facet-values-container">
                            <div
                              v-for="(facetValue, key) in facet.values"
                              :key="facetValue.value + facetValue.parentName"
                              class="searchstax-facet-value-container"
                              :class="{ 'searchstax-facet-value-disabled': facetValue.disabled }"
                              :ref="el => { facetContainers[key + facet.name] = el }"
                            >
                              <div class="searchstax-facet-input">
                                <input
                                  type="checkbox"
                                  class="searchstax-facet-input-checkbox"
                                  :checked="isChecked(facetValue)"
                                  :disabled="facetValue.disabled"
                                  @click="selectFacet(key + facet.name, $event, facetValue, true)"
                                />
                              </div>
                              <div
                                class="searchstax-facet-value-label"
                                @click="selectFacet(key + facet.name, $event, facetValue, false)"
                              >{{ facetValue.value }}</div>
                              <div
                                class="searchstax-facet-value-count"
                                @click="selectFacet(key + facet.name, $event, facetValue, false)"
                              >({{ facetValue.count }})</div>
                            </div>
                            <div
                              class="searchstax-facet-show-more-container"
                              v-if="facet.hasMoreFacets"
                            >
                              <div
                                class="searchstax-facet-show-more-container"
                                @click="showMoreLessDesktop($event, facet)"
                              >
                                <div
                                  v-if="facet.showingAllFacets"
                                  class="searchstax-facet-show-less-button searchstax-facet-show-button"
                                >less</div>
                                <div
                                  v-else
                                  class="searchstax-facet-show-more-button  searchstax-facet-show-button"
                                >more</div>
                              </div>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                    <button
                      class="searchstax-facets-mobile-overlay-done"
                      @click="closeOverlay"
                    >Done</button>
                  </div>
                </div>
              </template>
            </SearchstaxFacetsWidget>

SearchFeedback Widget

example of search feedback widget initialization with minimum options

<SearchstaxSearchFeedbackWidget></SearchstaxSearchFeedbackWidget>

example of search feedback widget initialization with template overrides

<SearchstaxSearchFeedbackWidget>
            <template #searchFeedback="{ searchFeedbackData, onOriginalQueryClick }">
              <div
                class="searchstax-feedback-container"
                v-if="searchFeedbackData && searchFeedbackData?.searchExecuted && searchFeedbackData?.totalResults"
              > Showing <b>{{ searchFeedbackData.startResultIndex }} - {{ searchFeedbackData.endResultIndex }}</b> of
                <b>{{ searchFeedbackData.totalResults }}</b> results <span v-if="searchFeedbackData.searchTerm">for "<b>{{
                  searchFeedbackData.searchTerm }}</b>" </span>
                <div class="searchstax-feedback-container-suggested">
                  <div v-if="searchFeedbackData.autoCorrectedQuery"> Search instead for <a
                      href="#"
                      @click.prevent="onOriginalQueryClick($event)"
                      class="searchstax-feedback-original-query"
                    >{{ searchFeedbackData.originalQuery }}</a>
                  </div>
                </div>
              </div>
            </template>
          </SearchstaxSearchFeedbackWidget>

RelatedSearches widget

example of search feedback widget initialization with minimum options

<SearchstaxRelatedSearchesWidget
              :relatedSearchesURL="config.relatedSearchesURL"
              :relatedSearchesAPIKey="config.relatedSearchesAPIKey"
            ></SearchstaxRelatedSearchesWidget>

example of search feedback widget initialization with template overrides

<SearchstaxRelatedSearchesWidget
              :relatedSearchesURL="config.relatedSearchesURL"
              :relatedSearchesAPIKey="config.relatedSearchesAPIKey"
            >
              <template #related="{ relatedData, executeSearch }">
                <div
                  class="searchstax-related-searches-container"
                  id="searchstax-related-searches-container"
                  v-if="relatedData && relatedData?.searchExecuted && relatedData?.hasRelatedSearches"
                > Related searches: <span id="searchstax-related-searches"></span>
                  <span
                    class="searchstax-related-search"
                    v-if="relatedData.relatedSearches"
                  >
                    <span
                      v-for="related in relatedData.relatedSearches"
                      :key="related.related_search"
                      @click="executeSearch(related)"
                      class="searchstax-related-search searchstax-related-search-item"
                    > {{ related.related_search }}<span v-if="!related.last">,</span>
                    </span>
                  </span>
                </div>
              </template>
            </SearchstaxRelatedSearchesWidget>

ExternalPromotions widget

example of search feedback widget initialization with minimum options

<SearchstaxExternalPromotionsWidget></SearchstaxExternalPromotionsWidget>

example of search feedback widget initialization with template overrides

<SearchstaxExternalPromotionsWidget>
              <template #externalPromotions="{ externalPromotionsData, trackClick }">
                <div
                  class="searchstax-external-promotions-container"
                  id="searchstax-external-promotions-container"
                  v-if="externalPromotionsData && externalPromotionsData?.searchExecuted && externalPromotionsData?.hasExternalPromotions"
                >
                  <div
                    class="searchstax-external-promotion searchstax-search-result"
                    v-for="externalPromotion in externalPromotionsData.externalPromotions"
                    :key="externalPromotion.id"
                  >
                    <div class="icon-elevated"></div>
                    <a
                      v-if="externalPromotion.url"
                      href="{{externalPromotion.url}}"
                      @click="trackClick(externalPromotion, $event)"
                      class="searchstax-result-item-link"
                    ></a>
                    <div class="searchstax-search-result-title-container">
                      <span class="searchstax-search-result-title">{{ externalPromotion.name }}</span>
                    </div>
                    <p
                      v-if="externalPromotion.description"
                      class="searchstax-search-result-description searchstax-search-result-common"
                    > {{ externalPromotion.description }} </p>
                    <p
                      v-if="externalPromotion.url"
                      class="searchstax-search-result-description searchstax-search-result-common"
                    > {{ externalPromotion.url }} </p>
                  </div>
                </div>
              </template>
            </SearchstaxExternalPromotionsWidget>

Sorting Widget

example of sorting widget initialization with minimum options

<SearchstaxSortingWidget></SearchstaxSortingWidget>

example of sorting widget initialization with template override

<SearchstaxSortingWidget>
            <template #sorting="{ sortingData, orderChange, selectedSorting }">
              <div
                class="searchstax-sorting-container"
                v-if="sortingData && sortingData?.searchExecuted && sortingData?.hasResultsOrExternalPromotions"
              >
                <label
                  class="searchstax-sorting-label"
                  for="sort-by"
                >Sort By</label>
                <select
                  id="searchstax-search-order-select"
                  class="searchstax-search-order-select"
                  :value="selectedSorting"
                  @change="orderChange($event)"
                >
                  <option value=""> Relevance </option>
                  <option value="date desc"> Newest Content </option>
                  <option value="date asc"> Oldest Content </option>
                </select>
              </div>
            </template>
          </SearchstaxSortingWidget>

Template overrides

Templates use vue slots.

STYLING

scss styles can be imported from searchstudio-ux-js

 @import './../node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/scss/mainTheme.scss';

css can be taken from

./../node_modules/@searchstax-inc/searchstudio-ux-js/dist/styles/mainTheme.css
1.0.2

9 months ago

1.0.1

9 months ago

0.2.27

10 months ago

0.2.26

10 months ago

0.2.25

11 months ago

0.2.24

12 months ago

0.2.18

12 months ago

0.2.16

1 year ago

0.2.14

1 year ago

0.2.8

1 year ago

0.2.7

1 year ago

0.2.6

1 year ago

0.2.5

1 year ago

0.2.4

1 year ago

0.2.2

1 year ago

0.2.1

2 years ago

0.1.2

2 years ago

0.2.0

2 years ago

0.1.1

2 years ago

0.1.3

2 years ago

0.0.14

2 years ago

0.0.13

2 years ago

0.0.12

2 years ago

0.0.11

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago