mirror of https://github.com/requarks/wiki
parent
c87f4ce770
commit
5c6965b544
@ -0,0 +1,224 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
q-toolbar(
|
||||||
|
style='height: 64px;'
|
||||||
|
dark
|
||||||
|
v-if='siteStore.features.search'
|
||||||
|
)
|
||||||
|
q-input(
|
||||||
|
dark
|
||||||
|
v-model='siteStore.search'
|
||||||
|
standout='bg-white text-dark'
|
||||||
|
dense
|
||||||
|
rounded
|
||||||
|
ref='searchField'
|
||||||
|
style='width: 100%;'
|
||||||
|
label='Search...'
|
||||||
|
@keyup.enter='onSearchEnter'
|
||||||
|
@focus='state.searchIsFocused = true'
|
||||||
|
@blur='checkSearchFocus'
|
||||||
|
)
|
||||||
|
template(v-slot:prepend)
|
||||||
|
q-circular-progress.q-mr-xs(
|
||||||
|
v-if='siteStore.searchIsLoading && route.path !== `/_search`'
|
||||||
|
instant-feedback
|
||||||
|
indeterminate
|
||||||
|
rounded
|
||||||
|
color='primary'
|
||||||
|
size='20px'
|
||||||
|
)
|
||||||
|
q-icon(v-else, name='las la-search')
|
||||||
|
template(v-slot:append)
|
||||||
|
q-badge.q-mr-sm(
|
||||||
|
v-if='!state.searchIsFocused'
|
||||||
|
label='Ctrl+K'
|
||||||
|
color='grey-7'
|
||||||
|
outline
|
||||||
|
@click='searchField.focus()'
|
||||||
|
)
|
||||||
|
q-badge.q-mr-sm(
|
||||||
|
v-else-if='siteStore.search && siteStore.search !== siteStore.searchLastQuery'
|
||||||
|
label='Press Enter'
|
||||||
|
color='grey-7'
|
||||||
|
outline
|
||||||
|
@click='searchField.focus()'
|
||||||
|
)
|
||||||
|
q-icon.cursor-pointer(
|
||||||
|
name='las la-times'
|
||||||
|
size='20px'
|
||||||
|
@click='siteStore.search=``'
|
||||||
|
v-if='siteStore.search.length > 0'
|
||||||
|
color='grey-6'
|
||||||
|
)
|
||||||
|
.searchpanel(
|
||||||
|
ref='searchPanel'
|
||||||
|
v-if='searchPanelIsShown'
|
||||||
|
)
|
||||||
|
template(v-if='siteStore.tagsLoaded && siteStore.tags.length > 0')
|
||||||
|
.searchpanel-header
|
||||||
|
span Popular Tags
|
||||||
|
q-space
|
||||||
|
q-btn.acrylic-btn(
|
||||||
|
flat
|
||||||
|
label='View All'
|
||||||
|
rounded
|
||||||
|
size='xs'
|
||||||
|
)
|
||||||
|
.flex.q-mb-md
|
||||||
|
q-chip(
|
||||||
|
v-for='tag of popularTags'
|
||||||
|
square
|
||||||
|
color='grey-8'
|
||||||
|
text-color='white'
|
||||||
|
icon='las la-hashtag'
|
||||||
|
size='sm'
|
||||||
|
clickable
|
||||||
|
@click='addTag(tag)'
|
||||||
|
) {{ tag }}
|
||||||
|
.searchpanel-header Search Operators
|
||||||
|
.searchpanel-tip #[code !foo] or #[code -bar] to exclude "foo" and "bar".
|
||||||
|
.searchpanel-tip #[code bana*] for to match any term starting with "bana" (e.g. banana).
|
||||||
|
.searchpanel-tip #[code foo,bar] or #[code foo|bar] to search for "foo" OR "bar".
|
||||||
|
.searchpanel-tip #[code "foo bar"] to match exactly the phrase "foo bar".
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useQuasar } from 'quasar'
|
||||||
|
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { orderBy } from 'lodash-es'
|
||||||
|
|
||||||
|
import { useSiteStore } from 'src/stores/site'
|
||||||
|
|
||||||
|
// QUASAR
|
||||||
|
|
||||||
|
const $q = useQuasar()
|
||||||
|
|
||||||
|
// STORES
|
||||||
|
|
||||||
|
const siteStore = useSiteStore()
|
||||||
|
|
||||||
|
// ROUTER
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// I18N
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
searchIsFocused: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchPanel = ref(null)
|
||||||
|
const searchField = ref(null)
|
||||||
|
|
||||||
|
// COMPUTED
|
||||||
|
|
||||||
|
const searchPanelIsShown = computed(() => {
|
||||||
|
return state.searchIsFocused && (siteStore.search !== siteStore.searchLastQuery || siteStore.search === '')
|
||||||
|
})
|
||||||
|
|
||||||
|
const popularTags = computed(() => {
|
||||||
|
return orderBy(siteStore.tags, ['usageCount', 'desc']).map(t => t.tag)
|
||||||
|
})
|
||||||
|
|
||||||
|
// WATCHERS
|
||||||
|
|
||||||
|
watch(searchPanelIsShown, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
siteStore.fetchTags()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
function handleKeyPress (ev) {
|
||||||
|
if (siteStore.features.search) {
|
||||||
|
if (ev.ctrlKey && ev.key === 'k') {
|
||||||
|
ev.preventDefault()
|
||||||
|
searchField.value.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSearchEnter () {
|
||||||
|
if (!siteStore.search) { return }
|
||||||
|
if (route.path === '/_search') {
|
||||||
|
router.replace({ path: '/_search', query: { q: siteStore.search } })
|
||||||
|
} else {
|
||||||
|
siteStore.searchIsLoading = true
|
||||||
|
router.push({ path: '/_search', query: { q: siteStore.search } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSearchFocus (ev) {
|
||||||
|
if (!searchPanel.value?.contains(ev.relatedTarget)) {
|
||||||
|
state.searchIsFocused = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTag (tag) {
|
||||||
|
if (!siteStore.search.includes(`#${tag}`)) {
|
||||||
|
siteStore.search = siteStore.search ? `${siteStore.search} #${tag}` : `#${tag}`
|
||||||
|
}
|
||||||
|
searchField.value.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MOUNTED
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (process.env.CLIENT) {
|
||||||
|
window.addEventListener('keydown', handleKeyPress)
|
||||||
|
}
|
||||||
|
if (route.path.startsWith('/_search')) {
|
||||||
|
searchField.value.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (process.env.CLIENT) {
|
||||||
|
window.removeEventListener('keydown', handleKeyPress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.searchpanel {
|
||||||
|
position: absolute;
|
||||||
|
top: 64px;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgba(0,0,0,.7);
|
||||||
|
border-radius: 0 0 12px 12px;
|
||||||
|
color: #FFF;
|
||||||
|
padding: .5rem 1rem 1rem;
|
||||||
|
width: 100%;
|
||||||
|
backdrop-filter: blur(7px);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,.2);
|
||||||
|
padding: 0 0 .5rem 0;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tip {
|
||||||
|
+ .searchpanel-tip {
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: rgba(0,0,0,.7);
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in new issue