AlgoliaSearchBox.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <div class="algolia-search-box" id="docsearch" />
  3. </template>
  4. <script setup lang="ts">
  5. import '@docsearch/css'
  6. import { useRoute, useRouter } from 'vitepress'
  7. import { defineProps, getCurrentInstance, onMounted, watch } from 'vue'
  8. import docsearch from '@docsearch/js'
  9. import type { DefaultTheme } from '../config'
  10. import type { DocSearchHit } from '@docsearch/react/dist/esm/types'
  11. import { useSiteDataByRoute } from 'vitepress'
  12. const siteData = useSiteDataByRoute()
  13. const props = defineProps<{
  14. options: DefaultTheme.AlgoliaSearchOptions
  15. multilang?: boolean
  16. }>()
  17. const vm = getCurrentInstance()
  18. const route = useRoute()
  19. const router = useRouter()
  20. watch(
  21. () => props.options,
  22. (value) => {
  23. update(value)
  24. }
  25. )
  26. onMounted(() => {
  27. initialize(props.options)
  28. })
  29. function isSpecialClick(event: MouseEvent) {
  30. return (
  31. event.button === 1 ||
  32. event.altKey ||
  33. event.ctrlKey ||
  34. event.metaKey ||
  35. event.shiftKey
  36. )
  37. }
  38. function getRelativePath(absoluteUrl: string) {
  39. const { pathname, hash } = new URL(absoluteUrl)
  40. return pathname + hash
  41. }
  42. function update(options: any) {
  43. if (vm && vm.vnode.el) {
  44. vm.vnode.el.innerHTML =
  45. '<div class="algolia-search-box" id="docsearch"></div>'
  46. initialize(options)
  47. }
  48. }
  49. function initialize(userOptions: any) {
  50. // if the user has multiple locales, the search results should be filtered
  51. // based on the language
  52. const facetFilters = props.multilang
  53. ? ['language:' + siteData.value.lang]
  54. : []
  55. docsearch(
  56. Object.assign({}, userOptions, {
  57. container: '#docsearch',
  58. searchParameters: Object.assign({}, userOptions.searchParameters, {
  59. // pass a custom lang facetFilter to allow multiple language search
  60. // https://github.com/algolia/docsearch-configs/pull/3942
  61. facetFilters: facetFilters.concat(
  62. userOptions.searchParameters?.facetFilters || []
  63. )
  64. }),
  65. navigator: {
  66. navigate: ({ suggestionUrl }: { suggestionUrl: string }) => {
  67. const { pathname: hitPathname } = new URL(
  68. window.location.origin + suggestionUrl
  69. )
  70. // Router doesn't handle same-page navigation so we use the native
  71. // browser location API for anchor navigation
  72. if (route.path === hitPathname) {
  73. window.location.assign(window.location.origin + suggestionUrl)
  74. } else {
  75. router.go(suggestionUrl)
  76. }
  77. }
  78. },
  79. transformItems: (items: DocSearchHit[]) => {
  80. return items.map((item) => {
  81. return Object.assign({}, item, {
  82. url: getRelativePath(item.url)
  83. })
  84. })
  85. },
  86. hitComponent: ({
  87. hit,
  88. children
  89. }: {
  90. hit: DocSearchHit
  91. children: any
  92. }) => {
  93. const relativeHit = hit.url.startsWith('http')
  94. ? getRelativePath(hit.url as string)
  95. : hit.url
  96. return {
  97. type: 'a',
  98. ref: undefined,
  99. constructor: undefined,
  100. key: undefined,
  101. props: {
  102. href: hit.url,
  103. onClick: (event: MouseEvent) => {
  104. if (isSpecialClick(event)) {
  105. return
  106. }
  107. // we rely on the native link scrolling when user is already on
  108. // the right anchor because Router doesn't support duplicated
  109. // history entries
  110. if (route.path === relativeHit) {
  111. return
  112. }
  113. // if the hits goes to another page, we prevent the native link
  114. // behavior to leverage the Router loading feature
  115. if (route.path !== relativeHit) {
  116. event.preventDefault()
  117. }
  118. router.go(relativeHit)
  119. },
  120. children
  121. }
  122. }
  123. }
  124. })
  125. )
  126. }
  127. </script>
  128. <style>
  129. .algolia-search-box {
  130. padding-top: 1px;
  131. }
  132. @media (min-width: 720px) {
  133. .algolia-search-box {
  134. padding-left: 8px;
  135. }
  136. }
  137. @media (min-width: 751px) {
  138. .algolia-search-box {
  139. min-width: 176.3px; /* avoid layout shift */
  140. }
  141. .algolia-search-box .DocSearch-Button-Placeholder {
  142. padding-left: 8px;
  143. font-size: 0.9rem;
  144. font-weight: 500;
  145. }
  146. }
  147. .DocSearch {
  148. --docsearch-primary-color: var(--c-brand);
  149. --docsearch-highlight-color: var(--docsearch-primary-color);
  150. --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
  151. --docsearch-text-color: var(--c-text-light);
  152. --docsearch-muted-color: var(--c-text-lighter);
  153. --docsearch-searchbox-background: #f2f2f2;
  154. }
  155. </style>