feat: search feature working well!

This commit is contained in:
2025-07-21 15:05:28 +01:00
parent 251e2bc553
commit 68010503ab
3 changed files with 45 additions and 50 deletions

View File

@ -26,7 +26,7 @@ export const SearchCard = (props: { item: UserImage }) => {
return ( return (
<A <A
href={`/entity/${props.item.data.ID}`} href={`/entity/${props.item.data.ID}`}
class="h-[144px] border relative border-neutral-200 cursor-pointer overflow-hidden rounded-xl" class="w-full h-[144px] border relative border-neutral-200 cursor-pointer overflow-hidden rounded-xl"
> >
<UnwrappedSearchCard item={props.item} /> <UnwrappedSearchCard item={props.item} />
</A> </A>

View File

@ -1,8 +1,9 @@
import { Component, createSignal } from "solid-js"; import { Component, createSignal, For } from "solid-js";
import { Search } from "@kobalte/core/search"; import { Search } from "@kobalte/core/search";
import { IconSearch } from "@tabler/icons-solidjs"; import { IconSearch } from "@tabler/icons-solidjs";
import { useSearch } from "./search"; import { useSearch } from "./search";
import { UserImage } from "@network/index"; import { UserImage } from "@network/index";
import { SearchCard } from "@components/search-card/SearchCard";
export const SearchPage: Component = () => { export const SearchPage: Component = () => {
const fuse = useSearch(); const fuse = useSearch();
@ -13,16 +14,12 @@ export const SearchPage: Component = () => {
<Search <Search
options={searchItems()} options={searchItems()}
onInputChange={(e) => { onInputChange={(e) => {
const items = fuse().search(e); setSearchItems(
setSearchItems(items.map((i) => i.item.image)); fuse()
.search(e)
.map((i) => i.item),
);
}} }}
itemComponent={(props) => (
<Search.Item item={props.item}>
<Search.ItemLabel>
{JSON.stringify(props.item.rawValue)}
</Search.ItemLabel>
</Search.Item>
)}
> >
<Search.Label /> <Search.Label />
<Search.Control class="flex"> <Search.Control class="flex">
@ -37,9 +34,9 @@ export const SearchPage: Component = () => {
/> />
</Search.Control> </Search.Control>
<Search.Portal> <Search.Portal>
<Search.Content class="w-full rounded-xl bg-white p-4"> <Search.Content class="container relative w-full rounded-xl bg-white p-4 grid grid-cols-3 gap-4">
<Search.Arrow /> <Search.Arrow />
<Search.Listbox /> <For each={searchItems()}>{(item) => <SearchCard item={item} />}</For>
<Search.NoResult>No result found</Search.NoResult> <Search.NoResult>No result found</Search.NoResult>
</Search.Content> </Search.Content>
</Search.Portal> </Search.Portal>

View File

@ -1,47 +1,45 @@
import { useSearchImageContext } from "@contexts/SearchImageContext"; import { useSearchImageContext } from "@contexts/SearchImageContext";
import { UserImage } from "@network/index"; import { UserImage } from "@network/index";
import { createMemo } from "solid-js";
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import { createEffect } from "solid-js";
const getSearchTerms = (image: UserImage): Array<string> => { // This language is stupid. `keyof` only returns common keys but this somehow doesnt.
switch (image.type) { type KeysOfUnion<T> = T extends T ? keyof T : never;
case "location":
return [ const weightedTerms: Record<
image.data.Name, KeysOfUnion<UserImage["data"]>,
image.data.Description, number | undefined
image.data.Address, > = {
image.data.CreatedAt, ID: undefined,
].filter((i) => i != null); LocationID: undefined,
case "event": OrganizerID: undefined,
return [ Images: undefined,
image.data.Name,
image.data.Description, Name: 5,
image.data.CreatedAt, Description: 2,
].filter((i) => i != null); Address: 2,
case "contact":
return [ PhoneNumber: 2,
image.data.Name, Email: 2,
image.data.Description,
image.data.CreatedAt, CreatedAt: 1,
image.data.Email, StartDateTime: 1,
image.data.PhoneNumber, EndDateTime: 1,
].filter((i) => i != null);
case "note": Content: 1,
return [
image.data.Name,
image.data.Description,
image.data.CreatedAt,
image.data.Content,
].filter((i) => i != null);
}
}; };
export const useSearch = () => { export const useSearch = () => {
const { images } = useSearchImageContext(); const { images } = useSearchImageContext();
const searchTerms = createMemo(() => return () =>
images().map((i) => ({ image: i, searchTerms: getSearchTerms(i) })), new Fuse(images(), {
); shouldSort: true,
keys: Object.entries(weightedTerms)
return () => new Fuse(searchTerms(), { keys: ["searchTerms"] }); .filter(([, w]) => w != null)
.map(([name, weight]) => ({
name: `data.${name}`,
weight,
})),
});
}; };