docs_vue2/components/OneSearch.vue
2021-12-27 13:15:01 +08:00

191 lines
4.7 KiB
Vue

<template>
<div class="one-search">
<veui-search-box
ref="search"
v-model="query"
class="input"
:placeholder="searchPlaceholder"
@input="handleInput"
/>
<div
ref="docsearch"
class="docsearch"
/>
</div>
</template>
<script>
import { SearchBox } from 'veui'
import { createElement } from 'preact'
import i18n from '../common/i18n'
function isSpecialClick (event) {
return (
event.button === 1 ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
)
}
function normalizeURL (url) {
if (url.endsWith('/')) {
return url.slice(0, -1)
}
return url.replace(/\/([#?])/, '$1')
}
function wait (time) {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
export default {
name: 'one-search',
components: {
'veui-search-box': SearchBox
},
mixins: [i18n],
data () {
return {
preparing: true,
query: ''
}
},
computed: {
searchPlaceholder () {
return this.locale === 'zh-Hans' ? '⌘ K | 搜索…' : '⌘ K | Search...'
}
},
watch: {
locale (val) {
this.update(val)
}
},
mounted () {
this.initialize(this.locale)
},
methods: {
getRelativePath (absoluteUrl) {
const { pathname, hash } = new URL(absoluteUrl)
return `${pathname}/${hash}`
},
triggerFocus (e) {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
this.$refs.search.focus()
}
},
initialize (locale) {
document.addEventListener('keydown', this.triggerFocus)
Promise.all([
import(/* webpackChunkName: "docsearch" */ '@docsearch/js'),
import(/* webpackChunkName: "docsearch" */ '@docsearch/css')
]).then(([docsearch]) => {
document.removeEventListener('keydown', this.triggerFocus)
docsearch = docsearch.default
docsearch({
appId: 'WW5G10K3KO',
apiKey: '3dc7f443d11e6712da7f98b2720bb8fc',
indexName: 'veui',
container: this.$refs.docsearch,
searchParameters: {
facetFilters: [`lang:${locale}`]
},
navigator: {
navigate: ({ itemUrl }) => {
const { pathname: hitPathname } = new URL(
window.location.origin + itemUrl
)
if (this.$router.history.current.path === hitPathname) {
window.location.assign(window.location.origin + itemUrl)
} else {
this.$router.push(itemUrl)
}
}
},
transformItems: items => {
return items.map(item => {
return {
...item,
url: this.getRelativePath(item.url)
}
})
},
hitComponent: ({ hit, children }) => {
const url = normalizeURL(hit.url)
return createElement(
'a',
{
href: url,
onClick: event => {
if (isSpecialClick(event)) {
return
}
// We rely on the native link scrolling when user is
// already on the right anchor because Vue Router doesn't
// support duplicated history entries.
if (this.$router.history.current.fullPath === hit.url) {
return
}
const { pathname: hitPathname } = new URL(
window.location.origin + hit.url
)
// If the hits goes to another page, we prevent the native link behavior
// to leverage the Vue Router loading feature.
if (this.$router.history.current.path !== hitPathname) {
event.preventDefault()
}
this.$router.push(url)
},
children
}
)
}
})
this.preparing = false
if (this.query) {
this.teleportQuery(this.query)
}
})
},
update (locale) {
this.$refs.docsearch.innerHTML = ''
this.initialize(locale)
},
handleInput (value) {
if (!value || this.preparing) {
return
}
this.teleportQuery(value)
},
teleportQuery (value) {
this.query = ''
const docsearchButton = this.$refs.docsearch.querySelector('.DocSearch')
docsearchButton.click()
setTimeout(() => {
const docsearchInput = document.getElementById('docsearch-input')
docsearchInput.value = value
docsearchInput.dispatchEvent(new Event('input'))
})
}
}
}
</script>
<style lang="stylus" scoped>
.input
width auto
.docsearch
display none
</style>