91 lines
4.1 KiB
Vue
91 lines
4.1 KiB
Vue
<script setup lang="ts">
|
|
import type { Search, File, Project, User } from '~/types/api';
|
|
|
|
const input = ref(''), results = ref<Search>({ projects: [], files: [], users: [] });
|
|
const pos = ref<DOMRect>(), loading = ref(false);
|
|
|
|
let timeout: NodeJS.Timeout;
|
|
|
|
const emit = defineEmits(['navigate']);
|
|
|
|
function search(e: Event)
|
|
{
|
|
pos.value = (e.currentTarget as HTMLElement)?.getBoundingClientRect();
|
|
|
|
loading.value = true;
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(debounced, 250);
|
|
}
|
|
async function debounced()
|
|
{
|
|
if(!input.value)
|
|
return;
|
|
|
|
try
|
|
{
|
|
const fetch = await useFetch(`/api/search`, {
|
|
query: {
|
|
"search": `%${input.value}%`
|
|
}
|
|
});
|
|
|
|
results.value = fetch.data.value;
|
|
}
|
|
catch (e) {
|
|
results.value = { projects: [], files: [], users: [] };
|
|
}
|
|
|
|
loading.value = false;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="w-full border border-light-30 dark:border-dark-30">
|
|
<Input class="w-full" type="text" placeholder="Recherche" v-model="input" @input="search" />
|
|
</div>
|
|
<Teleport to="#teleports" v-if="input !== '' && !!pos">
|
|
<div class="absolute z-50 border border-light-35 dark:border-dark-35 bg-light-0 dark:bg-dark-0 text-light-100 dark:text-dark-100 divide-y divide-light-35 dark:divide-dark-35 max-h-96 overflow-auto
|
|
scroll"
|
|
:style="{top: (pos.bottom + 4) + 'px', left: pos.left + 'px', width: pos.width + 'px'}">
|
|
<div class="loading" v-if="loading"></div>
|
|
<template v-else>
|
|
<div class="cursor-pointer hover:bg-light-25 dark:hover:bg-dark-25 px-4 py-1 " v-for="result of results.projects" :key="result.id"
|
|
@mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')"
|
|
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
|
@mousedown.prevent="navigateTo(`/explorer/${result.id}/${result.home}`); input = ''; emit('navigate');">
|
|
<div class="">
|
|
<Highlight class="text-lg" :text="result.name" :matched="input" />
|
|
<div class="flex justify-between text-sm">
|
|
<div class="">{{ result.username }}</div>
|
|
<div class="">{{ result.pages }} pages</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="cursor-pointer hover:bg-light-25 dark:hover:bg-dark-25 px-4 py-1 " v-for="result of results.files" :key="result.id"
|
|
@mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')"
|
|
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
|
@mousedown.prevent="navigateTo(`/explorer/${result.project}/${result.path}`); input = ''; emit('navigate');">
|
|
<div class="">
|
|
<Highlight class="text-lg" :text="result.title" :matched="input" />
|
|
<div class="flex justify-between text-sm">
|
|
<div class="">{{ result.username }}</div>
|
|
<div class="">{{ result.comments }} commentaires</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="cursor-pointer hover:bg-light-25 dark:hover:bg-dark-25 px-4 py-1 " v-for="result of results.users" :key="result.id"
|
|
@mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')"
|
|
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
|
@mousedown.prevent="navigateTo(`/users/${result.id}`); input = ''; emit('navigate');">
|
|
<div class="">
|
|
<Highlight class="text-lg" :text="result.username" :matched="input" />
|
|
</div>
|
|
</div>
|
|
<div class=""
|
|
v-if="results.projects.length === 0 && results.files.length === 0 && results.users.length === 0">
|
|
Aucun résultat
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</Teleport>
|
|
</template> |