{"version":3,"file":"predictive-search-b529217b.js","sources":["../../../../src/scripts/coveo/components/predictive-search.tsx"],"sourcesContent":["import React, { useId, useState, useEffect, useRef } from 'react';\nimport { buildSearchEngine, buildStandaloneSearchBox } from '@coveo/headless';\nimport { rovingIndex } from 'roving-ux';\nimport { createDataLayerString } from '../../helpers/helpers';\nimport { ExtendedWindowType } from '../engine';\nimport { secureStorage } from '../../helpers/global-storage';\n\nconst wrapper = document.getElementsByClassName('coveo-predictive-search')[0];\n\nconst { dataset } = wrapper as HTMLElement;\n\nconst coveoData = JSON.parse(dataset.coveo as string);\nconst optionsData = JSON.parse(dataset.options as string);\n\nconst config = {\n  organizationId: coveoData.OrganizationId,\n  accessToken: coveoData.ApiKey,\n  search: {\n    searchHub: coveoData.SearchHubPipeline,\n    pipeline: coveoData.SearchHubPipeline\n  }\n};\n\nconst engine = buildSearchEngine({\n  configuration: config\n});\n\nconst controller = buildStandaloneSearchBox(engine, {\n  options: {\n    ...optionsData,\n    highlightOptions: {\n      exactMatchDelimiters: {\n        open: '<strong>',\n        close: '</strong>'\n      }\n    }\n  }\n});\n\ntype PredictiveSearchProps = {\n  placeholder: string;\n  containerClass?: string;\n  fieldClass?: string;\n  submitLabel: string;\n  storageKey: string;\n  viewAllLabel: string;\n  layer?: string;\n  clickDataLayer?: string;\n  viewDataLayer?: string;\n  clearLabel?: string;\n};\n\nfunction PredictiveSearch({\n  placeholder,\n  fieldClass,\n  submitLabel,\n  containerClass,\n  storageKey,\n  viewAllLabel,\n  layer,\n  clickDataLayer,\n  viewDataLayer,\n  clearLabel\n}: PredictiveSearchProps) {\n  const id = useId();\n  const [state, setState] = useState(controller.state);\n  const [value, setValue] = useState(controller.state.value);\n  const [showingSuggestions, setShowingSuggestions] = useState(false);\n  const [focus, setFocus] = useState(false);\n  const listRef = useRef<HTMLUListElement>(null);\n  const input = useRef<HTMLInputElement>(null);\n  const clickDataLayerObj = clickDataLayer\n    ? JSON.parse(clickDataLayer)\n    : undefined;\n  const viewDataLayerObj = viewDataLayer\n    ? JSON.parse(viewDataLayer)\n    : undefined;\n\n  const isEnterKey = (e: React.KeyboardEvent<HTMLFormElement>) =>\n    e.key === 'Enter';\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLFormElement>) => {\n    if (input.current === document.activeElement) {\n      if (isEnterKey(e)) {\n        e.preventDefault();\n        controller.updateText(value);\n        controller.submit();\n        setFocus(false);\n      }\n\n      if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n        e.preventDefault();\n        if (listRef.current) {\n          if (e.key === 'ArrowDown') {\n            const firstElement = listRef.current.querySelector('li');\n            if (firstElement) {\n              firstElement.querySelector('button')?.focus();\n              setFocus(true);\n            }\n          } else {\n            const lastElement = listRef.current.querySelector('li:last-child');\n            if (lastElement) {\n              (lastElement as HTMLLIElement).querySelector('button')?.focus();\n              setFocus(true);\n            }\n          }\n        }\n      }\n\n      if (e.key === 'Escape') {\n        e.preventDefault();\n        input.current?.blur();\n        setFocus(false);\n      }\n    } else {\n      if (e.key === 'Escape') {\n        e.preventDefault();\n        input.current?.focus();\n        setFocus(false);\n      }\n      if (\n        (e.key === 'ArrowDown' || e.key === 'ArrowUp') &&\n        e.currentTarget.querySelector('.field--search')\n      ) {\n        e.preventDefault();\n      }\n    }\n  };\n\n  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    controller.updateText(e.target.value);\n    setValue(e.target.value);\n    setFocus(true);\n  };\n\n  const handleSuggestionClick = (value: string) => {\n    setValue(value);\n    controller.selectSuggestion(value);\n  };\n\n  const handleMouseDown = (e: Event) => {\n    if ((e.target as HTMLElement)?.closest('.predictive-search__inner')) {\n      return;\n    } else {\n      setFocus(false);\n    }\n  };\n\n  const handleClear = () => {\n    setValue('');\n    controller.updateText('');\n  };\n\n  const handleInputBlur = (e: React.FocusEvent<HTMLElement>) => {\n    if (\n      !(e.relatedTarget as HTMLElement)?.closest('.predictive-search__inner')\n    ) {\n      setFocus(false);\n    }\n  };\n\n  useEffect(() => {\n    controller.subscribe(() => setState(controller.state));\n  }, []);\n\n  useEffect(() => {\n    if (listRef.current) {\n      rovingIndex({\n        element: listRef.current,\n        target: '.predictive-search__item'\n      });\n\n      setShowingSuggestions(true);\n    } else {\n      setShowingSuggestions(false);\n    }\n  }, [state.suggestions]);\n\n  useEffect(() => {\n    if (input.current) {\n      input.current.focus();\n    }\n\n    window.addEventListener('mousedown', e => handleMouseDown(e));\n    return () => {\n      window.removeEventListener('mousedown', e => handleMouseDown(e));\n    };\n  }, [input.current]);\n\n  useEffect(() => {\n    if (showingSuggestions) {\n      const global = window as ExtendedWindowType;\n\n      global.dataLayer?.push(viewDataLayerObj);\n    }\n  }, [showingSuggestions]);\n\n  if (!state) {\n    return null;\n  }\n\n  if (state.redirectTo) {\n    const { redirectTo, value, analytics } = state;\n    const data = JSON.stringify({ value, analytics });\n    secureStorage.set(storageKey, data);\n    window.location.href = redirectTo;\n  }\n\n  const getViewAllDataLayer = () => {\n    if (clickDataLayerObj) {\n      clickDataLayerObj.event_attributes.search_term =\n        viewAllLabel.toLowerCase();\n    }\n\n    return clickDataLayerObj;\n  };\n\n  return (\n    <form\n      className={`predictive-search ${containerClass}`}\n      onSubmit={e => {\n        e.preventDefault();\n      }}\n      onKeyDown={e => handleKeyDown(e)}\n    >\n      <div className={`field field--required ${fieldClass}`}>\n        <label\n          className=\"field__label sr-only\"\n          htmlFor={`predictive-search-${id}`}\n        ></label>\n        <div className='predictive-search__inner'>\n          <input\n            value={state.value}\n            className=\"field__input\"\n            type=\"text\"\n            id={`predictive-search-${id}`}\n            name={`predictive-search-${id}`}\n            placeholder={placeholder}\n            required\n            onChange={handleInputChange}\n            onInput={handleInputChange}\n            ref={input}\n            onFocus={() => setFocus(true)}\n            onBlur={e => handleInputBlur(e)}\n          />\n          {state.suggestions.length > 0 && state.value && focus && (\n            <ul\n              className=\"predictive-search__list\"\n              ref={listRef}\n              onFocus={() => setFocus(true)}\n              onBlur={handleInputBlur}\n            >\n              {state.suggestions.map(suggestion => {\n                const value = suggestion.rawValue;\n\n                if (clickDataLayerObj) {\n                  clickDataLayerObj.event_attributes.search_term =\n                    value.toLowerCase();\n                }\n\n                const dataLayer = clickDataLayerObj;\n                return (\n                  <li key={value}>\n                    <button\n                      className=\"predictive-search__item\"\n                      title={value}\n                      onClick={() => handleSuggestionClick(value)}\n                      dangerouslySetInnerHTML={{\n                        __html: suggestion.highlightedValue\n                      }}\n                      data-layer={\n                        dataLayer\n                          ? createDataLayerString([dataLayer])\n                          : undefined\n                      }\n                    />\n                  </li>\n                );\n              })}\n              <li>\n                <button\n                  className=\"link link--arrow link--small predictive-search__item\"\n                  title={viewAllLabel}\n                  onClick={() => {\n                    controller.updateText(value);\n                    controller.submit();\n                  }}\n                  onFocus={() => {\n                    setFocus(true);\n                  }}\n                  data-layer={\n                    clickDataLayerObj\n                      ? createDataLayerString([getViewAllDataLayer()])\n                      : undefined\n                  }\n                >\n                  <span>{viewAllLabel}</span>\n                </button>\n              </li>\n            </ul>\n          )}\n        </div>\n\n        {state.value.length > 0 && (\n          <button\n            className=\"field__input-close\"\n            title={clearLabel}\n            aria-label={clearLabel}\n            onClick={handleClear}\n          ></button>\n        )}\n      </div>\n      <button\n        disabled={state.value.length === 0}\n        type=\"submit\"\n        className=\"btn btn--primary\"\n        onClick={() => {\n          controller.updateText(value);\n          controller.submit();\n        }}\n        {...(layer && { 'data-layer': layer })}\n      >\n        {submitLabel}\n      </button>\n    </form>\n  );\n}\n\nexport default PredictiveSearch;\n"],"names":["wrapper","dataset","coveoData","optionsData","config","engine","buildSearchEngine","controller","buildStandaloneSearchBox","PredictiveSearch","placeholder","fieldClass","submitLabel","containerClass","storageKey","viewAllLabel","layer","clickDataLayer","viewDataLayer","clearLabel","id","useId","state","setState","useState","value","setValue","showingSuggestions","setShowingSuggestions","focus","setFocus","listRef","useRef","input","clickDataLayerObj","viewDataLayerObj","isEnterKey","handleKeyDown","firstElement","_a","lastElement","_b","_c","_d","handleInputChange","handleSuggestionClick","handleMouseDown","handleClear","handleInputBlur","useEffect","rovingIndex","redirectTo","analytics","data","secureStorage","getViewAllDataLayer","React","suggestion","dataLayer","createDataLayerString"],"mappings":"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"}