haditha_ui/components/haditha/AutoComplation.vue
2025-03-15 13:41:29 +03:30

452 lines
10 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import type { InputMenuItem } from "@nuxt/ui";
import hadithaApi from "../../apis/hadithaApi";
import type { HadithResponseModel } from "../../types/hadithType";
import { useStorage } from "@vueuse/core";
import type { MaybeArrayOfArray } from "@nuxt/ui/runtime/types/utils.js";
// #region props
const props = defineProps({
showFilter: {
default: false,
},
showPrevSearch: {
default: false,
},
});
// #endregion props
// #region emits
const emit = defineEmits(["response-ready"]);
// #endregion emits
// #region refs
const userSearchHistory = useStorage(
"userSearchHistory",
new Set() // Initial value
);
const searchTerm = ref("");
const open = ref(false);
const loading = ref(false);
// If you want to share state across multiple components,
// you can use the same key in useState. Nuxt will ensure
// that the state is shared and reactive across your application.
// const typingTimer = useState<number>("typingTimer", () => 0);
// const doneTypingInterval = useState<number>("doneTypingInterval", () => 1000);
const typingTimer = ref<number | any>(0);
const doneTypingInterval = ref<number>(1000);
// #endregion refs
// #region reactive
const state = reactive({
list: [],
filters: [
{
label: "معنایی",
items: [],
},
{
label: "ترجمه",
items: [],
},
{
label: "مترادف",
items: [
{
label: "جستجو در همه",
},
{
label: "فقط در متن عربی حدیث",
},
{
label: "فقط در ترجمه ها",
},
{
label: "فقط در شروح",
},
],
},
{
label: "نوع",
items: [
{
label: "جستجو در همه",
},
{
label: "فقط در متن عربی حدیث",
},
{
label: "فقط در ترجمه ها",
},
{
label: "فقط در شروح",
},
],
},
{
label: "عین عبارت",
items: [
{
label: "جستجو در همه",
},
{
label: "فقط در متن عربی حدیث",
},
{
label: "فقط در ترجمه ها",
},
{
label: "فقط در شروح",
},
],
},
],
});
// #endregion reactive
// #region methods
const clearSimilar = () => {
console.info("clearSimilar");
};
const onBlur = () => {
console.info("onBlur");
};
const onChange = () => {
console.info("onChange");
sendQuery();
};
const onUpdateModel = (newVal: boolean | InputMenuItem | any) => {
console.info("onUpdateModel", newVal);
};
const onKeyDown = () => {
clearTimeout(typingTimer.value);
};
const onKeyUp = () => {
clearTimeout(typingTimer.value);
typingTimer.value = setTimeout(() => {
sendQuery();
}, doneTypingInterval.value);
};
const sendQuery = async () => {
if (loading.value) return;
loading.value = true;
let url = hadithaApi.search;
url = url.replace("@index_key", "dhparag");
url = url.replace("@search_type", "normal");
url = url.replace("@type_key", "hadith");
url = url.replace("@offset", "0");
url = url.replace("@limit", "10");
url = url.replace("@listkey", "normal");
url = url.replace("@field_collapsed", "normal");
url = url.replace(
"@q=none",
searchTerm.value.length ? `q=${searchTerm.value}` : "q=none"
);
// fetch search list from backend(ssr)
const { data, status, error, refresh, clear } =
await useHadithaSearchComposable<HadithResponseModel>(url, {
method: "post",
});
if (status.value == "success") {
loading.value = false;
}
// pass res and search query to the parent.
emit("response-ready", {
res: data.value,
searchQuery: searchTerm.value,
});
// check if search term is not empty
if (searchTerm.value) userSearchHistory.value.add(searchTerm.value); // Add the value to the Set
// close the history dropdown menu
open.value = false;
};
// #endregion methods
</script>
<template>
<div class="haditha-search-root-wrapper">
<div class="haditha-search-root">
<div v-if="showPrevSearch" class="prev-search-item flex items-center">
<span class="total">۴۷ مشابه </span>
<span class="text me-auto">
عَنِ الْحَسَنِ بْنِ عَلِيِّ بْنِ يُوسُفَ، عَنْ جَدِّهِ، قَالَ:
</span>
<UButton
icon="i-lucide:x"
color="neutral"
variant="ghost"
class="clear-similar-btn"
@click="clearSimilar"
/>
</div>
<div class="search-input">
<UInputMenu
class="w-full focus:placeholder-gray-800"
:items="<any>Array.from(userSearchHistory)"
v-model="searchTerm"
v-model:open="open"
v-model:search-term="searchTerm"
placeholder="هوشمند جستجو کنید..."
:ui="{
base: 'haditha-search-input',
}"
:content="{
align: 'start',
side: 'bottom',
sideOffset: 4,
}"
:loading="loading"
highlight
highlightOnHover
@focus="open = true"
@blur="open = false"
@change="onChange"
@update:modelValue="onUpdateModel"
@update:searchTerm="onUpdateModel"
@keydown="onKeyDown"
@keyup="onKeyUp"
@keydown.enter="sendQuery"
>
</UInputMenu>
</div>
<UButton
class="my-trailing-button"
@click.prevent="sendQuery"
icon="i-haditha-search"
>
<!-- <UIcon name="i-lucide-search" /> -->
</UButton>
</div>
<div class="search-filter my-3 space-x-2" v-if="props.showFilter">
<UDropdownMenu
v-for="(filter, index) in state.filters"
:items="filter.items"
:content="{
align: 'start',
side: 'bottom',
sideOffset: 8,
}"
:ui="{
content: 'w-48',
}"
>
<UButton
class="filter-item"
:label="filter.label"
:trailingIcon="filter.items?.length ? 'i-haditha-chevron-down' : ''"
/>
</UDropdownMenu>
</div>
</div>
</template>
<style scoped>
.haditha-search-root-wrapper {
max-width: 656px;
width: 100%;
margin: 0 1em;
.haditha-search-root {
position: relative;
&::before {
content: "";
position: absolute;
left: 1em;
right: 1em;
top: 50%;
backdrop-filter: blur(60px);
background: linear-gradient(137.41deg, #ffffff -42.82%, #e5e0ff 87.9%);
filter: blur(60px);
width: 626px;
height: 68px;
z-index: 0;
}
.prev-search-item {
width: 328;
height: 49;
gap: 6px;
border-radius: 12px;
border-width: 0.5px;
padding-top: 8px;
padding-right: 12px;
padding-bottom: 8px;
padding-left: 12px;
background: #626b84;
border: 0.5px solid;
margin-bottom: 0.7em;
border-image-source: linear-gradient(
102.02deg,
#4be8ae 7.38%,
#00a762 91.78%
);
.total {
width: 53;
height: 24;
gap: 4px;
border-radius: 6px;
padding: 5px 7px;
background: #1b213266;
font-family: IRANSansX;
font-weight: 500;
font-size: 10px;
line-height: 15px;
letter-spacing: 0%;
text-align: right;
color: #ffffff;
}
.text {
font-family: Takrim;
font-weight: 400;
font-size: 16px;
line-height: 32px;
letter-spacing: 0%;
text-align: right;
color: #ffffff;
}
.clear-similar-btn {
width: 32px;
height: 32px;
gap: 4px;
border-radius: 60px;
padding-top: 11px;
padding-right: 6px;
padding-bottom: 11px;
padding-left: 6px;
background: #1b213266;
color: #fff;
}
}
.search-input {
position: relative;
}
.search-filter {
.filter-item {
/* width: 81px; */
height: 40px;
border-radius: 12px;
border-width: 0.3px;
padding-top: 8px;
padding-right: 12px;
padding-bottom: 8px;
padding-left: 12px;
gap: 4px;
background-color: #fff;
border: 0.3px solid #e0e0e0;
box-shadow: 0px 1px 4px 0px #0000000d;
color: #8a92a8;
font-family: IRANSansX;
font-weight: 400;
font-size: 13px;
line-height: 20px;
letter-spacing: 0%;
text-align: right;
&.active {
color: linear-gradient(102.02deg, #4be8ae 7.38%, #00a762 91.78%);
* {
color: #fff;
}
}
}
}
&.search-page {
&::before {
content: none;
}
.my-trailing-button {
/* width: 32px; */
/* height: 32px; */
}
.haditha-search-input {
height: 56px;
}
}
}
}
</style>
<style>
.haditha-search-root-wrapper {
.my-trailing-button {
position: absolute;
z-index: 1;
width: 48px;
height: 48px;
justify-content: center;
align-items: center;
padding: 0;
border-radius: 50px;
background: linear-gradient(320.71deg, #b9fde0 6.56%, #e4f9f0 69.69%);
left: 12px;
top: 0;
bottom: 0;
margin: auto;
transition: all 0.2s ease-in-out;
&:hover {
transition: all 0.2s ease-in-out;
background: linear-gradient(320.71deg, #54ecaa 6.56%, #b6f0d9 69.69%);
}
& > span {
/* width: 18px; */
/* height: 18px; */
/* background-image: linear-gradient(
102.02deg,
#4be8ae 7.38%,
#00a762 91.78%
); */
}
}
.haditha-search-input {
z-index: 0;
height: 72px;
justify-content: space-between;
padding-top: 12px;
padding-right: 12px;
padding-bottom: 12px;
padding-left: 12px;
border-radius: 12px;
border-width: 0.3px;
background-color: #fff;
border: 0.3px solid #e0e0e0;
box-shadow: 0px 1px 4px 0px #0000000d;
font-family: IRANSansX;
font-weight: 300;
font-size: 14px;
line-height: 21px;
letter-spacing: 0%;
text-align: right;
color: #a7acbe;
}
}
</style>