https://github.com/user-attachments/assets/fc4d15f9-2b54-4627-940f-94772ec739b1 --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
172 lines
4.0 KiB
Vue
172 lines
4.0 KiB
Vue
<script>
|
|
import SearchSuggestions from './SearchSuggestions.vue';
|
|
import PublicSearchInput from './PublicSearchInput.vue';
|
|
|
|
import ArticlesAPI from '../api/article';
|
|
|
|
export default {
|
|
components: {
|
|
PublicSearchInput,
|
|
SearchSuggestions,
|
|
},
|
|
props: {
|
|
size: {
|
|
type: String,
|
|
default: 'default',
|
|
validator: value => ['small', 'default'].includes(value),
|
|
},
|
|
showKbd: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
emits: ['input', 'blur'],
|
|
data() {
|
|
return {
|
|
searchTerm: '',
|
|
isLoading: false,
|
|
showSearchBox: false,
|
|
searchResults: [],
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
portalSlug() {
|
|
return window.portalConfig.portalSlug;
|
|
},
|
|
localeCode() {
|
|
return window.portalConfig.localeCode;
|
|
},
|
|
normalizedSearchTerm() {
|
|
return this.searchTerm.trim();
|
|
},
|
|
shouldShowSearchBox() {
|
|
return this.normalizedSearchTerm !== '' && this.showSearchBox;
|
|
},
|
|
searchTranslations() {
|
|
const { searchTranslations = {} } = window.portalConfig;
|
|
return searchTranslations;
|
|
},
|
|
kbdLabel() {
|
|
if (!this.showKbd) return '';
|
|
const isMac = /Mac|iPhone|iPad|iPod/i.test(
|
|
navigator.platform || navigator.userAgent
|
|
);
|
|
return isMac ? '⌘ K' : 'Ctrl K';
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
currentPage() {
|
|
this.clearSearchTerm();
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
if (this.showKbd) document.addEventListener('keydown', this.onKeydown);
|
|
},
|
|
|
|
unmounted() {
|
|
if (this.showKbd) document.removeEventListener('keydown', this.onKeydown);
|
|
clearTimeout(this.typingTimer);
|
|
},
|
|
|
|
methods: {
|
|
onUpdateSearchTerm(value) {
|
|
this.searchTerm = value;
|
|
if (this.typingTimer) {
|
|
clearTimeout(this.typingTimer);
|
|
}
|
|
|
|
if (this.normalizedSearchTerm === '') {
|
|
this.searchResults = [];
|
|
this.isLoading = false;
|
|
this.closeSearch();
|
|
return;
|
|
}
|
|
|
|
this.openSearch();
|
|
this.isLoading = true;
|
|
this.typingTimer = setTimeout(() => {
|
|
this.fetchArticlesByQuery();
|
|
}, 1000);
|
|
},
|
|
onChange(e) {
|
|
this.$emit('input', e.target.value);
|
|
},
|
|
onBlur(e) {
|
|
this.$emit('blur', e.target.value);
|
|
},
|
|
openSearch() {
|
|
this.showSearchBox = true;
|
|
},
|
|
closeSearch() {
|
|
this.showSearchBox = false;
|
|
},
|
|
clearSearchTerm() {
|
|
this.searchTerm = '';
|
|
},
|
|
onKeydown(e) {
|
|
if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
|
|
e.preventDefault();
|
|
if (this.$refs.searchInput) this.$refs.searchInput.focusInput();
|
|
}
|
|
if (e.key === 'Escape') {
|
|
this.closeSearch();
|
|
if (this.$refs.searchInput) this.$refs.searchInput.blurInput();
|
|
}
|
|
},
|
|
async fetchArticlesByQuery() {
|
|
const query = this.normalizedSearchTerm;
|
|
if (!query) {
|
|
this.isLoading = false;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
this.isLoading = true;
|
|
this.searchResults = [];
|
|
const { data } = await ArticlesAPI.searchArticles(
|
|
this.portalSlug,
|
|
this.localeCode,
|
|
query
|
|
);
|
|
this.searchResults = data.payload;
|
|
} catch (error) {
|
|
// Show something wrong message
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div v-on-clickaway="closeSearch" class="relative w-full max-w-5xl my-4">
|
|
<PublicSearchInput
|
|
ref="searchInput"
|
|
:search-term="searchTerm"
|
|
:search-placeholder="searchTranslations.searchPlaceholder"
|
|
:size="size"
|
|
:kbd="kbdLabel"
|
|
@update:search-term="onUpdateSearchTerm"
|
|
@focus="openSearch"
|
|
/>
|
|
<div
|
|
v-if="shouldShowSearchBox"
|
|
class="absolute w-full top-14"
|
|
@mouseover="openSearch"
|
|
>
|
|
<SearchSuggestions
|
|
:items="searchResults"
|
|
:is-loading="isLoading"
|
|
:search-term="normalizedSearchTerm"
|
|
:empty-placeholder="searchTranslations.emptyPlaceholder"
|
|
:results-title="searchTranslations.resultsTitle"
|
|
:loading-placeholder="searchTranslations.loadingPlaceholder"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|