mirror of
https://github.com/0x2E/fusion.git
synced 2025-06-08 05:27:15 +09:00
feat: add search page (#110)
This commit is contained in:
parent
5e9d2d8d07
commit
78e4666be3
3 changed files with 80 additions and 49 deletions
|
@ -1,60 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { listItems } from '$lib/api/item';
|
||||
import type { Item } from '$lib/api/model';
|
||||
import { debounce } from '$lib/utils';
|
||||
import { Search } from 'lucide-svelte';
|
||||
import ItemList from './ItemList.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { t } from '$lib/i18n';
|
||||
|
||||
let modal = $state<HTMLDialogElement>();
|
||||
let keyword = $state('');
|
||||
let result = $state<{ total: number; items: Item[] } | null>();
|
||||
// let isMac = navigator.platform.indexOf('Mac') === 0 || navigator.platform === 'iPhone';
|
||||
|
||||
const handleSearch = debounce(async () => {
|
||||
const resp = await listItems(); // TODO filter
|
||||
result = resp;
|
||||
}, 500);
|
||||
import { Search } from 'lucide-svelte';
|
||||
</script>
|
||||
|
||||
<label class="input input-sm lg:w-80">
|
||||
<Search class="size-4 opacity-50" />
|
||||
<input
|
||||
type="search"
|
||||
class="input"
|
||||
placeholder={t('item.search.placeholder')}
|
||||
onclick={() => modal?.showModal()}
|
||||
onclick={async () => {
|
||||
await goto('/search');
|
||||
}}
|
||||
readonly
|
||||
class="cursor-pointer"
|
||||
/>
|
||||
<!-- <kbd class="kbd kbd-sm">{isMac ? '⌘' : '^'}</kbd>
|
||||
<kbd class="kbd kbd-sm">K</kbd> -->
|
||||
</label>
|
||||
|
||||
<dialog id="search" bind:this={modal} class="modal modal-bottom sm:modal-middle">
|
||||
<div class="modal-box min-h-80 w-full overflow-x-hidden sm:max-w-4xl">
|
||||
<form method="dialog">
|
||||
<button class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2">✕</button>
|
||||
</form>
|
||||
<h3 class="text-lg font-bold">{t('common.search')}</h3>
|
||||
<div class="py-4">
|
||||
<label class="input w-full">
|
||||
<Search class="size-4 opacity-50" />
|
||||
<input
|
||||
type="search"
|
||||
required
|
||||
placeholder={t('item.search.placeholder')}
|
||||
bind:value={keyword}
|
||||
oninput={handleSearch}
|
||||
class="w-full"
|
||||
/>
|
||||
</label>
|
||||
{#if result?.total}
|
||||
<div class="mt-6">
|
||||
<ItemList items={result.items} total={result.total} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
|
58
frontend/src/routes/(authed)/search/+page.svelte
Normal file
58
frontend/src/routes/(authed)/search/+page.svelte
Normal file
|
@ -0,0 +1,58 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { applyFilterToURL, parseURLtoFilter } from '$lib/api/item';
|
||||
import ItemList from '$lib/components/ItemList.svelte';
|
||||
import ItemListPlaceholder from '$lib/components/ItemListPlaceholder.svelte';
|
||||
import PageNavHeader from '$lib/components/PageNavHeader.svelte';
|
||||
import { t } from '$lib/i18n';
|
||||
import { Search } from 'lucide-svelte';
|
||||
|
||||
let { data } = $props();
|
||||
let filterForm = $state(Object.assign({}, parseURLtoFilter(page.url.searchParams)));
|
||||
|
||||
async function handleSearch(e: Event) {
|
||||
e.preventDefault();
|
||||
|
||||
const url = page.url;
|
||||
applyFilterToURL(url, filterForm);
|
||||
console.log(url.toString());
|
||||
goto(url, {
|
||||
invalidate: ['page:search']
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{t('common.search')}: {filterForm.keyword}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<PageNavHeader title={t('common.search')}></PageNavHeader>
|
||||
<div class="px-4 lg:px-8">
|
||||
<div class="py-6">
|
||||
<h1 class="text-3xl font-bold">{t('common.search')}: {filterForm.keyword}</h1>
|
||||
</div>
|
||||
<form onsubmit={handleSearch} class="w-full max-w-lg pb-4">
|
||||
<div class="join w-full">
|
||||
<div class="w-full">
|
||||
<label class="input join-item w-full">
|
||||
<Search class="size-4 opacity-50" />
|
||||
<input
|
||||
type="search"
|
||||
placeholder={t('item.search.placeholder')}
|
||||
bind:value={filterForm.keyword}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary join-item">{t('common.search')}</button>
|
||||
</div>
|
||||
</form>
|
||||
{#await data.items}
|
||||
<ItemListPlaceholder />
|
||||
{:then items}
|
||||
<ItemList items={items.items} total={items.total} highlightUnread={true} />
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
15
frontend/src/routes/(authed)/search/+page.ts
Normal file
15
frontend/src/routes/(authed)/search/+page.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { listItems, parseURLtoFilter } from '$lib/api/item';
|
||||
import { fullItemFilter } from '$lib/state.svelte';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ url, depends }) => {
|
||||
depends('page:search');
|
||||
|
||||
const filter = parseURLtoFilter(url.searchParams);
|
||||
Object.assign(fullItemFilter, filter);
|
||||
|
||||
return {
|
||||
filter,
|
||||
items: listItems(filter)
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue