-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
549 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<script lang="ts"> | ||
import { getQueryParts } from '$lib/client/helpers/search'; | ||
import { normalizeQuery } from '$lib/shared/helpers/search'; | ||
export let query: string; | ||
export let fullText: string; | ||
const fullTextParts = getQueryParts(normalizeQuery(fullText), normalizeQuery(query)); | ||
</script> | ||
|
||
<p> | ||
{#each fullTextParts as { text, type }} | ||
<span class={type === 'highlight' ? 'bg-yellow-400' : ''}>{text}</span> | ||
{/each} | ||
</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<script lang="ts"> | ||
import { getGlobalSearchResults } from '$lib/client/api/search'; | ||
import { | ||
GLOBAL_SEARCH_INPUT_ELEMENT_ID, | ||
SEARCH_DEBOUNCE_TIMEOUT_MS | ||
} from '$lib/client/constants/search'; | ||
import { FAILURE_TOAST_OPTIONS } from '$lib/client/constants/toasts'; | ||
import { debounce, memoize } from '$lib/client/helpers/util'; | ||
import { searchModalActiveStore } from '$lib/client/stores/layout'; | ||
import { queryStore } from '$lib/client/stores/search'; | ||
import type { IAppSearchResult } from '$lib/shared/types/search'; | ||
import { toast } from '@zerodevx/svelte-toast'; | ||
import { Modal, Spinner } from 'flowbite-svelte'; | ||
import { onDestroy } from 'svelte'; | ||
import Searchbar from '../reusable/Searchbar.svelte'; | ||
import SearchResultsContainer from './SearchResultsContainer.svelte'; | ||
let currentSearchResults: IAppSearchResult | null = null; | ||
let searchResultsLoading = false; | ||
const searchModalUnsubsribe = searchModalActiveStore.subscribe((active) => { | ||
if (active) { | ||
const globalSearchInput = document.querySelector( | ||
`#${GLOBAL_SEARCH_INPUT_ELEMENT_ID}` | ||
) as HTMLInputElement | null; | ||
if (globalSearchInput) { | ||
globalSearchInput.focus(); | ||
} | ||
} else { | ||
currentSearchResults = null; | ||
} | ||
}); | ||
const fetchQueryResults = memoize(async (query: string) => { | ||
const response = await getGlobalSearchResults(query); | ||
if (response.ok) { | ||
return (await response.json()) as IAppSearchResult; | ||
} else { | ||
toast.push( | ||
'An unexpected error occured while retrieving the search results', | ||
FAILURE_TOAST_OPTIONS | ||
); | ||
return null; | ||
} | ||
}, true); | ||
const debouncedFetchQueryResults = debounce(async (query: string) => { | ||
searchResultsLoading = true; | ||
queryStore.set(query); | ||
currentSearchResults = query ? await fetchQueryResults(query as never) : null; | ||
searchResultsLoading = false; | ||
}, SEARCH_DEBOUNCE_TIMEOUT_MS) as (query: string) => void; | ||
onDestroy(() => { | ||
searchModalUnsubsribe(); | ||
}); | ||
</script> | ||
|
||
<Modal | ||
title="Find tags, artists, users and posts" | ||
open={$searchModalActiveStore} | ||
outsideclose | ||
class="w-screen" | ||
placement="top-center" | ||
on:close={() => searchModalActiveStore.set(false)} | ||
> | ||
<div class="flex relative"> | ||
<Searchbar | ||
inputElementId={GLOBAL_SEARCH_INPUT_ELEMENT_ID} | ||
isGlobal | ||
width="100%" | ||
placeholder="Enter your search query" | ||
queryHandler={debouncedFetchQueryResults} | ||
/> | ||
{#if searchResultsLoading} | ||
<Spinner color="pink" class="absolute top-2 right-2" size="7" /> | ||
{/if} | ||
</div> | ||
|
||
{#if currentSearchResults} | ||
<SearchResultsContainer results={currentSearchResults} /> | ||
{/if} | ||
</Modal> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,32 @@ | ||
<script lang="ts"> | ||
import { getGlobalSearchResults } from '$lib/client/api/search'; | ||
import { FAILURE_TOAST_OPTIONS } from '$lib/client/constants/toasts'; | ||
import type { IAppSearchResult } from '$lib/shared/types/search'; | ||
import { toast } from '@zerodevx/svelte-toast'; | ||
import { Input } from 'flowbite-svelte'; | ||
import { searchModalActiveStore } from '$lib/client/stores/layout'; | ||
import { Button, Input, Kbd } from 'flowbite-svelte'; | ||
import { SearchOutline } from 'flowbite-svelte-icons'; | ||
let currentQuery: string; | ||
let currentSearchResults: IAppSearchResult[] = []; | ||
const handleSearchQueryChange = async (event: Event) => { | ||
const target = event.target as HTMLInputElement; | ||
currentQuery = target.value; | ||
const response = await getGlobalSearchResults(currentQuery); | ||
if (response.ok) { | ||
currentSearchResults = (await response.json()) as IAppSearchResult[]; | ||
} else { | ||
toast.push('An error occured while trying to process that query', FAILURE_TOAST_OPTIONS); | ||
} | ||
}; | ||
</script> | ||
|
||
<div class="hidden relative md:block"> | ||
<div class="hidden relative md:block mt-1" id="global-searchbar"> | ||
<div class="flex absolute inset-y-0 start-0 items-center ps-3 pointer-events-none"> | ||
<SearchOutline class="w-4 h-4" /> | ||
</div> | ||
<Input class="ps-10" placeholder="Search..." /> | ||
<div class="flex space-x-2 absolute inset-y-0 end-8 items-center ps-3 pointer-events-none"> | ||
<Kbd class="p-1">CTRL</Kbd> | ||
<Kbd class="p-1">K</Kbd> | ||
</div> | ||
<Button | ||
on:click={() => searchModalActiveStore.set(true)} | ||
color="alternative" | ||
class="p-0 border-none opacity-50 hover:opacity-100" | ||
> | ||
<Input | ||
readonly | ||
class="ps-10 hover:cursor-pointer border-none outline-none caret-transparent focus:ring-0 focus:ring-offset-0" | ||
placeholder="Search" | ||
/> | ||
</Button> | ||
</div> | ||
|
||
<style> | ||
#global-searchbar { | ||
width: 250px; | ||
} | ||
</style> |
48 changes: 48 additions & 0 deletions
48
src/lib/client/components/search/SearchResultsContainer.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<script lang="ts"> | ||
import type { IAppSearchResult } from '$lib/shared/types/search'; | ||
import { TabItem, Tabs } from 'flowbite-svelte'; | ||
import LabelTable from '../tables/LabelTable.svelte'; | ||
import PostTable from '../tables/PostTable.svelte'; | ||
import UserTable from '../tables/UserTable.svelte'; | ||
export let results: IAppSearchResult; | ||
let { posts, artists, tags, users } = results; | ||
$: { | ||
posts = results.posts; | ||
artists = results.artists; | ||
tags = results.tags; | ||
users = results.users; | ||
} | ||
</script> | ||
|
||
<Tabs style="underline"> | ||
<TabItem open title="Tags"> | ||
{#if tags && tags.length > 0} | ||
<LabelTable labels={tags} labelType="tags" /> | ||
{:else} | ||
<p>No tags were found matching that query</p> | ||
{/if} | ||
</TabItem> | ||
<TabItem title="Artists"> | ||
{#if artists && artists.length > 0} | ||
<LabelTable labels={artists} labelType="artists" /> | ||
{:else} | ||
<p>No artists were found matching that query</p> | ||
{/if} | ||
</TabItem> | ||
<TabItem title="Posts"> | ||
{#if posts && posts.length > 0} | ||
<PostTable {posts} /> | ||
{:else} | ||
<p>No posts were found matching that query</p> | ||
{/if} | ||
</TabItem> | ||
<TabItem title="Users"> | ||
{#if users && users.length > 0} | ||
<UserTable {users} /> | ||
{:else} | ||
<p>No users were found matching that query</p> | ||
{/if} | ||
</TabItem> | ||
</Tabs> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<script lang="ts"> | ||
import type { IAppSearchResult } from '$lib/shared/types/search'; | ||
import { | ||
Table, | ||
TableBody, | ||
TableBodyCell, | ||
TableBodyRow, | ||
TableHead, | ||
TableHeadCell | ||
} from 'flowbite-svelte'; | ||
export let labels: IAppSearchResult['tags'] | IAppSearchResult['artists']; | ||
export let labelType: 'tags' | 'artists'; | ||
</script> | ||
|
||
<Table hoverable> | ||
<TableHead> | ||
<TableHeadCell>ID</TableHeadCell> | ||
<TableHeadCell>Name</TableHeadCell> | ||
<TableHeadCell> | ||
<span class="sr-only">Related posts</span> | ||
</TableHeadCell> | ||
</TableHead> | ||
<TableBody tableBodyClass="divide-y"> | ||
{#each labels || [] as label} | ||
<TableBodyRow> | ||
<TableBodyCell>{label.id}</TableBodyCell> | ||
<TableBodyCell>{label.name}</TableBodyCell> | ||
<TableBodyCell> | ||
<a | ||
href="/{labelType}/{label.name}" | ||
class="font-medium text-primary-600 hover:underline dark:text-primary-500" | ||
>Related posts</a | ||
> | ||
</TableBodyCell> | ||
</TableBodyRow> | ||
{/each} | ||
</TableBody> | ||
</Table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<script lang="ts"> | ||
import { formatDate } from '$lib/shared/helpers/dates'; | ||
import type { IAppSearchResult } from '$lib/shared/types/search'; | ||
import { | ||
Avatar, | ||
Table, | ||
TableBody, | ||
TableBodyCell, | ||
TableBodyRow, | ||
TableHead, | ||
TableHeadCell | ||
} from 'flowbite-svelte'; | ||
import HighlightedText from '../reusable/HighlightedText.svelte'; | ||
import { queryStore } from '$lib/client/stores/search'; | ||
export let posts: IAppSearchResult['posts']; | ||
</script> | ||
|
||
<Table hoverable> | ||
<TableHead> | ||
<TableHeadCell>ID</TableHeadCell> | ||
<TableHeadCell>Description</TableHeadCell> | ||
<TableHeadCell>Created at</TableHeadCell> | ||
<TableHeadCell>Author</TableHeadCell> | ||
<TableHeadCell> | ||
<span class="sr-only">View post</span> | ||
</TableHeadCell> | ||
</TableHead> | ||
<TableBody tableBodyClass="divide-y"> | ||
{#each posts || [] as post} | ||
<TableBodyRow> | ||
<TableBodyCell>{post.id}</TableBodyCell> | ||
<TableBodyCell tdClass="px-1 py-4 whitespace-wrap font-medium" | ||
> | ||
<HighlightedText fullText={post.description} query={$queryStore} /> | ||
</TableBodyCell | ||
> | ||
<TableBodyCell>{formatDate(new Date(post.createdAt))}</TableBodyCell> | ||
<TableBodyCell class="text-center"> | ||
<Avatar | ||
class="ml-auto mr-auto" | ||
src={post.uploaderProfilePictureUrl} | ||
alt="profile picture of {post.uploaderName}" | ||
/> | ||
<a | ||
href="/profile/{post.uploaderName}" | ||
class="font-medium text-primary-600 hover:underline dark:text-primary-500" | ||
>{post.uploaderName}</a | ||
> | ||
</TableBodyCell> | ||
<TableBodyCell> | ||
<a | ||
href="/posts/{post.id}" | ||
class="font-medium text-primary-600 hover:underline dark:text-primary-500">View post</a | ||
> | ||
</TableBodyCell> | ||
</TableBodyRow> | ||
{/each} | ||
</TableBody> | ||
</Table> |
Oops, something went wrong.