Guest User

Untitled

a guest
Feb 27th, 2024
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <html lang="en">
  2.   <head>
  3.     <meta charset="UTF-8" />
  4.     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  5.  
  6.     <link rel="shortcut icon" href="favicon.png" type="image/x-icon" />
  7.     <link
  8.       rel="stylesheet"
  9.       href="https://unpkg.com/instantsearch.css@7/themes/satellite-min.css"
  10.     />    
  11.     <link
  12.     rel="stylesheet"
  13.     href="style.css"
  14.   />
  15.     <link
  16.       rel="stylesheet"
  17.       href="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-theme-classic"
  18.     />
  19.     <title>InstantSearch | Autocomplete</title>
  20.   </head>
  21.  
  22.   <body>
  23.     <header class="header">
  24.       <div class="header-wrapper wrapper">
  25.         <nav class="header-nav">
  26.           <a href="/">Home</a>
  27.         </nav>
  28.         <div id="autocomplete"></div>
  29.       </div>
  30.     </header>
  31.  
  32.     <div class="container wrapper">
  33.       <div>
  34.         <div id="categories"></div>
  35.       </div>
  36.       <div>
  37.         <img src="iphone-banner.png" alt="iPhone banner" class="banner" />
  38.  
  39.         <div id="hits"></div>
  40.         <div id="pagination"></div>
  41.       </div>
  42.     </div>
  43.  
  44.     <!-- <script type="module" src="env.ts"></script>
  45.     <script type="module" src="src/app.ts"></script> -->
  46.  
  47.  
  48.  
  49.     <script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-js"></script>
  50.     <script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-plugin-query-suggestions"></script>
  51.     <script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-plugin-recent-searches"></script>
  52.  
  53.     <script
  54.   src="https://cdn.jsdelivr.net/npm/[email protected]/dist/algoliasearch.umd.js"
  55.   integrity="sha256-InehzjjnCqBlO9perxL3MsBXunj7aZttlr2ls+aH/N4="
  56.   crossorigin="anonymous"
  57. ></script>
  58.  
  59.     <script src="https://cdn.jsdelivr.net/npm/instantsearch.js" crossorigin="anonymous"></script>
  60.  
  61. <script type="module">
  62.   const { autocomplete } = window['@algolia/autocomplete-js'];
  63.   const { createQuerySuggestionsPlugin } = window['@algolia/autocomplete-plugin-query-suggestions'];
  64.   const { createLocalStorageRecentSearchesPlugin } = window['@algolia/autocomplete-plugin-recent-searches'];
  65.  
  66.   const { InstantSearch } = window['instantsearch'];
  67.  
  68. console.log(InstantSearch);
  69.   // const INSTANT_SEARCH_INDEX_NAME = 'mystock-prod';
  70.   // const INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE = 'childcategory_name';
  71.  
  72.    const INSTANT_SEARCH_INDEX_NAME = 'instant_search';
  73.    const INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE =
  74.   'hierarchicalCategories.lvl0';
  75.  
  76. const searchClient = algoliasearch(
  77.   'latency',
  78.   '6be0576ff61c053d5f9a3225e2a90f76'
  79. );
  80.  
  81.  
  82.  
  83. const { connectSearchBox } = instantsearch.connectors;
  84. const { instantSearchRouter } = instantsearch.routers.history();
  85.  
  86.  
  87.  
  88.   function debounce(fn, time) {
  89.   let timerId = undefined;
  90.  
  91.   return function (...args) {
  92.     if (timerId) {
  93.       clearTimeout(timerId);
  94.     }
  95.  
  96.     timerId = setTimeout(() => fn(...args), time);
  97.   };
  98. }
  99.  
  100. function setInstantSearchUiState(indexUiState) {
  101.   search.setUiState((uiState) => ({
  102.     ...uiState,
  103.     [INSTANT_SEARCH_INDEX_NAME]: {
  104.       ...uiState[INSTANT_SEARCH_INDEX_NAME],
  105.       // We reset the page when the search state changes.
  106.       page: 1,
  107.       ...indexUiState,
  108.     },
  109.   }));
  110. }
  111.  
  112.  
  113.   const debouncedSetInstantSearchUiState = debounce(
  114.   setInstantSearchUiState,
  115.   500
  116. );
  117.  
  118. function isModifierEvent(event) {
  119.   const isMiddleClick = event.button === 1;
  120.  
  121.   return (
  122.     isMiddleClick ||
  123.     event.altKey ||
  124.     event.ctrlKey ||
  125.     event.metaKey ||
  126.     event.shiftKey
  127.   );
  128. }
  129.  
  130.  
  131.  
  132.  
  133.  function getInstantSearchCurrentCategory() {
  134.   const indexUiState = search.getUiState()[INSTANT_SEARCH_INDEX_NAME];
  135.   const hierarchicalMenuUiState = indexUiState && indexUiState.hierarchicalMenu;
  136.   const currentCategories =
  137.     hierarchicalMenuUiState &&
  138.     hierarchicalMenuUiState[INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE];
  139.  
  140.   return currentCategories && currentCategories[0];
  141. }
  142.  
  143.  
  144.  
  145.  function getInstantSearchUrl(indexUiState) {
  146.   return search.createURL({ [INSTANT_SEARCH_INDEX_NAME]: indexUiState });
  147. }
  148.  
  149. function onSelect({ setIsOpen, setQuery, event, query, category }) {
  150.   // You want to trigger the default browser behavior if the event is modified.
  151.   if (isModifierEvent(event)) {
  152.     return;
  153.   }
  154.  
  155.   setQuery(query);
  156.   setIsOpen(false);
  157.   setInstantSearchUiState({
  158.     query,
  159.     hierarchicalMenu: {
  160.       [INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE]: [category],
  161.     },
  162.   });
  163. }
  164. function getItemUrl({ query, category }) {
  165.   return getInstantSearchUrl({
  166.     query,
  167.     hierarchicalMenu: {
  168.       [INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE]: [category],
  169.     },
  170.   });
  171. }
  172.  
  173. function getItemWrapper({ html, children, query, category }) {
  174.   const uiState = {
  175.     query,
  176.     hierarchicalMenu: {
  177.       [INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE]: [category],
  178.     },
  179.   };
  180.  
  181.   return html`<a
  182.     class="aa-ItemLink"
  183.     href=${getInstantSearchUrl(uiState)}
  184.     onClick=${(event) => {
  185.       if (!isModifierEvent(event)) {
  186.         // Bypass the original link behavior if there's no event modifier
  187.         // to set the InstantSearch UI state without reloading the page.
  188.         event.preventDefault();
  189.       }
  190.     }}
  191.   >
  192.     ${children}
  193.   </a>`;
  194. }
  195.  
  196.  
  197. const { panel } = instantsearch.widgets;
  198. const { configure } = instantsearch.widgets;
  199. const { hierarchicalMenu } = instantsearch.widgets;
  200. const { hits } = instantsearch.widgets;
  201. const { pagination } = instantsearch.widgets;
  202.  
  203.  
  204. const search = instantsearch({
  205.   searchClient,
  206.   indexName: INSTANT_SEARCH_INDEX_NAME,
  207.   routing: instantSearchRouter,
  208. });
  209. const virtualSearchBox = connectSearchBox(() => {});
  210. const hierarchicalMenuWithHeader = panel({
  211.   templates: { header: 'Categories' },
  212. })(hierarchicalMenu);
  213.  
  214.  
  215. search.addWidgets([
  216.   configure({
  217.     attributesToSnippet: ['name:7', 'description:15'],
  218.     snippetEllipsisText: '…',
  219.   }),
  220.   // Mount a virtual search box to manipulate InstantSearch's `query` UI
  221.   // state parameter.
  222.   virtualSearchBox(),
  223.   hierarchicalMenuWithHeader({
  224.     container: '#categories',
  225.     attributes: [
  226.       INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE,
  227.       'hierarchicalCategories.lvl1',
  228.     ],
  229.   }),
  230.   hits({
  231.     container: '#hits',
  232.     transformItems(items) {
  233.       return items.map((item) => ({
  234.         ...item,
  235.         category: item.categories[0],
  236.         comments: (item.popularity % 100).toLocaleString(),
  237.         sale: item.free_shipping,
  238.         // eslint-disable-next-line @typescript-eslint/camelcase
  239.         sale_price: item.free_shipping
  240.           ? (item.price - item.price / 10).toFixed(2)
  241.           : item.price.toString(),
  242.       }));
  243.     },
  244.     templates: {
  245.       item: (hit, { html, components }) => html`
  246.         <article class="hit">
  247.           <div class="hit-image">
  248.             <img src="${hit.image}" alt="${hit.name}" />
  249.           </div>
  250.           <div>
  251.             <h1>${components.Snippet({ hit, attribute: 'name' })}</h1>
  252.             <div>
  253.               By <strong>${hit.brand}</strong> in
  254.               <strong>${hit.category}</strong>
  255.             </div>
  256.           </div>
  257.  
  258.           <div
  259.             style="
  260.              display: grid;
  261.              grid-auto-flow: column;
  262.              justify-content: start;
  263.              align-items: center;
  264.              gap: 8px;
  265.            "
  266.           >
  267.             ${hit.rating > 0 &&
  268.             html`
  269.               <div
  270.                 style="
  271.                  display: grid;
  272.                  grid-auto-flow: column;
  273.                  justify-content: start;
  274.                  align-items: center;
  275.                  gap: 4px;
  276.                "
  277.               >
  278.                 <svg
  279.                   width="16"
  280.                   height="16"
  281.                   viewBox="0 0 24 24"
  282.                   fill="#e2a400"
  283.                   stroke="#e2a400"
  284.                   stroke-width="1"
  285.                   stroke-linecap="round"
  286.                   stroke-linejoin="round"
  287.                 >
  288.                   <polygon
  289.                     points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
  290.                   />
  291.                 </svg>
  292.                 ${hit.rating}
  293.               </div>
  294.             `}
  295.  
  296.             <div
  297.               style="
  298.                display: grid;
  299.                grid-auto-flow: column;
  300.                justify-content: start;
  301.                align-items: center;
  302.                gap: 4px;
  303.              "
  304.             >
  305.               <svg
  306.                 width="16"
  307.                 height="16"
  308.                 viewBox="0 0 24 24"
  309.                 fill="none"
  310.                 stroke="currentColor"
  311.                 stroke-width="2"
  312.                 stroke-linecap="round"
  313.                 stroke-linejoin="round"
  314.                 style="
  315.                  position: relative;
  316.                  top: 1px;
  317.                "
  318.               >
  319.                 <path
  320.                   d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
  321.                 ></path>
  322.               </svg>
  323.               <span>${hit.comments}</span>
  324.             </div>
  325.           </div>
  326.         </article>
  327.       `,
  328.     },
  329.   }),
  330.   pagination({
  331.     container: '#pagination',
  332.     padding: 2,
  333.     showFirst: false,
  334.     showLast: false,
  335.   }),
  336. ]);
  337.  
  338.  
  339. const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
  340.   key: 'instantsearch',
  341.   limit: 3,
  342.   transformSource: function({ source }) {
  343.     return {
  344.       ...source,
  345.       getItemUrl: function({ item }) {
  346.         return getItemUrl({
  347.           query: item.label,
  348.           category: item.category,
  349.         });
  350.       },
  351.       onSelect: function({ setIsOpen, setQuery, item, event }) {
  352.         onSelect({
  353.           setQuery,
  354.           setIsOpen,
  355.           event,
  356.           query: item.label,
  357.           category: item.category,
  358.         });
  359.       },
  360.       templates: {
  361.         ...source.templates,
  362.         item: function(params) {
  363.           const { children } = source.templates.item(params).props;
  364.           const { item, html } = params;
  365.  
  366.           return getItemWrapper({
  367.             query: item.label,
  368.             category: item.category,
  369.             html,
  370.             children,
  371.           });
  372.         },
  373.       },
  374.     };
  375.   },
  376. });
  377.  
  378. const querySuggestionsPluginInCategory = createQuerySuggestionsPlugin({
  379.   searchClient,
  380.   indexName: 'instant_search_demo_query_suggestions',
  381.   getSearchParams: function() {
  382.     const currentCategory = getInstantSearchCurrentCategory();
  383.  
  384.     return recentSearchesPlugin.data.getAlgoliaSearchParams({
  385.       hitsPerPage: 3,
  386.       facetFilters: [
  387.         `${INSTANT_SEARCH_INDEX_NAME}.facets.exact_matches.${INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE}.value:${currentCategory}`,
  388.       ],
  389.     });
  390.   },
  391.   transformSource: function({ source }) {
  392.     const currentCategory = getInstantSearchCurrentCategory();
  393.  
  394.     return {
  395.       ...source,
  396.       sourceId: 'querySuggestionsPluginInCategory',
  397.       getItemUrl: function({ item }) {
  398.         return getItemUrl({
  399.           query: item.query,
  400.           category: currentCategory,
  401.         });
  402.       },
  403.       onSelect: function({ setIsOpen, setQuery, event, item }) {
  404.         onSelect({
  405.           setQuery,
  406.           setIsOpen,
  407.           event,
  408.           query: item.query,
  409.           category: currentCategory,
  410.         });
  411.       },
  412.       getItems: function(params) {
  413.         if (!currentCategory) {
  414.           return [];
  415.         }
  416.  
  417.         return source.getItems(params);
  418.       },
  419.       templates: {
  420.         ...source.templates,
  421.         header: function({ html }) {
  422.           return html`
  423.             <span class="aa-SourceHeaderTitle">In ${currentCategory}</span>
  424.             <div class="aa-SourceHeaderLine" />
  425.           `;
  426.         },
  427.         item: function(params) {
  428.           const { children } = source.templates.item(params).props;
  429.           const { item, html } = params;
  430.  
  431.           return getItemWrapper({
  432.             query: item.query,
  433.             category: currentCategory,
  434.             html,
  435.             children,
  436.           });
  437.         },
  438.       },
  439.     };
  440.   },
  441. });
  442.  
  443. const querySuggestionsPlugin = createQuerySuggestionsPlugin({
  444.   searchClient,
  445.   indexName: 'instant_search_demo_query_suggestions',
  446.   getSearchParams: function() {
  447.     const currentCategory = getInstantSearchCurrentCategory();
  448.  
  449.     if (!currentCategory) {
  450.       return recentSearchesPlugin.data.getAlgoliaSearchParams({
  451.         hitsPerPage: 6,
  452.       });
  453.     }
  454.  
  455.     return recentSearchesPlugin.data.getAlgoliaSearchParams({
  456.       hitsPerPage: 3,
  457.       facetFilters: [
  458.         `${INSTANT_SEARCH_INDEX_NAME}.facets.exact_matches.${INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE}.value:-${currentCategory}`,
  459.       ],
  460.     });
  461.   },
  462.   categoryAttribute: [
  463.     INSTANT_SEARCH_INDEX_NAME,
  464.     'facets',
  465.     'exact_matches',
  466.     INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE,
  467.   ],
  468.   transformSource: function({ source }) {
  469.     const currentCategory = getInstantSearchCurrentCategory();
  470.  
  471.     return {
  472.       ...source,
  473.       sourceId: 'querySuggestionsPlugin',
  474.       getItemUrl: function({ item }) {
  475.         return getItemUrl({
  476.           query: item.query,
  477.           category: item.__autocomplete_qsCategory,
  478.         });
  479.       },
  480.       onSelect: function({ setIsOpen, setQuery, event, item }) {
  481.         onSelect({
  482.           setQuery,
  483.           setIsOpen,
  484.           event,
  485.           query: item.query,
  486.           category: item.__autocomplete_qsCategory,
  487.         });
  488.       },
  489.       getItems: function(params) {
  490.         if (!params.state.query) {
  491.           return [];
  492.         }
  493.  
  494.         return source.getItems(params);
  495.       },
  496.       templates: {
  497.         ...source.templates,
  498.         header: function({ html }) {
  499.           if (!currentCategory) {
  500.             return null;
  501.           }
  502.  
  503.           return html`
  504.             <span class="aa-SourceHeaderTitle">In other categories</span>
  505.             <div class="aa-SourceHeaderLine" />
  506.           `;
  507.         },
  508.         item: function(params) {
  509.           const { children } = source.templates.item(params).props;
  510.           const { item, html } = params;
  511.  
  512.           return getItemWrapper({
  513.             query: item.query,
  514.             category: item.__autocomplete_qsCategory,
  515.             html,
  516.             children,
  517.           });
  518.         },
  519.       },
  520.     };
  521.   },
  522. });
  523.  
  524. const searchPageState = getInstantSearchUiState();
  525.  
  526.  
  527. function startAutocomplete(searchInstance) {
  528.   let skipInstantSearchStateUpdate = false;
  529.  
  530.   const { setQuery } = autocomplete({
  531.     container: '#autocomplete',
  532.     placeholder: 'Search for products',
  533.     insights: true,
  534.     openOnFocus: true,
  535.     plugins: [
  536.       recentSearchesPlugin,
  537.       querySuggestionsPluginInCategory,
  538.       querySuggestionsPlugin,
  539.     ],
  540.     detachedMediaQuery: 'none',
  541.     initialState: {
  542.       query: searchPageState.query || '',
  543.     },
  544.     navigator: {
  545.       navigate: function() {
  546.         // We don't navigate to a new page because we leverage the InstantSearch
  547.         // UI state API.
  548.       },
  549.     },
  550.     onSubmit: function({ state }) {
  551.       setInstantSearchUiState({ query: state.query });
  552.     },
  553.     onReset: function() {
  554.       setInstantSearchUiState({
  555.         query: '',
  556.         hierarchicalMenu: {
  557.           [INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE]: [],
  558.         },
  559.       });
  560.     },
  561.     onStateChange: function({ prevState, state }) {
  562.       if (!skipInstantSearchStateUpdate && prevState.query !== state.query) {
  563.         debouncedSetInstantSearchUiState({ query: state.query });
  564.       }
  565.       skipInstantSearchStateUpdate = false;
  566.     },
  567.   });
  568.  
  569.   window.addEventListener('popstate', function() {
  570.     skipInstantSearchStateUpdate = true;
  571.     setQuery(
  572.       (searchInstance.helper && searchInstance.helper.state.query) || ''
  573.     );
  574.   });
  575. }
  576.  
  577.  
  578.  
  579.  
  580. search.start();
  581. startAutocomplete(search);
  582.  
  583.  
  584. function getInstantSearchUiState() {
  585.   const uiState = instantSearchRouter.read();
  586.  
  587.   return (uiState && uiState[INSTANT_SEARCH_INDEX_NAME]) || {};
  588. }
  589. </script>
  590.  
  591.  
  592.  
  593.   </body>
  594. </html>
  595.  
Add Comment
Please, Sign In to add comment