feat(search): enhance Search component with shortcuts and item modal

- Added functionality to fetch and display global shortcuts in the Search component.
- Introduced ItemModal for displaying detailed information about selected items.
- Updated SearchCard components to improve layout and information presentation.
- Enhanced user experience with better styling and accessibility features.
This commit is contained in:
2025-04-14 10:03:37 +02:00
parent 55614b34c7
commit 18f85a8929
8 changed files with 133 additions and 72 deletions

Binary file not shown.

View File

@ -26,7 +26,9 @@
"fuse.js": "^7.1.0", "fuse.js": "^7.1.0",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"solid-js": "^1.9.3", "solid-js": "^1.9.3",
"solid-markdown": "^2.0.14",
"solid-motionone": "^1.0.3", "solid-motionone": "^1.0.3",
"solidjs-markdown": "^0.2.0",
"tailwind-scrollbar-hide": "^2.0.0", "tailwind-scrollbar-hide": "^2.0.0",
"valibot": "^1.0.0-rc.2" "valibot": "^1.0.0-rc.2"
}, },

View File

@ -1,8 +1,8 @@
import { Button } from "@kobalte/core/button"; import { Button } from "@kobalte/core/button";
import { A } from "@solidjs/router";
import { IconSearch, IconSettings } from "@tabler/icons-solidjs"; import { IconSearch, IconSettings } from "@tabler/icons-solidjs";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import clsx from "clsx";
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import { import {
For, For,
@ -12,14 +12,13 @@ import {
onCleanup, onCleanup,
onMount, onMount,
} from "solid-js"; } from "solid-js";
import { ImageViewer } from "./components/ImageViewer";
import { SearchCard } from "./components/search-card/SearchCard"; import { SearchCard } from "./components/search-card/SearchCard";
import { SearchCardContact } from "./components/search-card/SearchCardContact";
import { SearchCardEvent } from "./components/search-card/SearchCardEvent"; import { invoke } from "@tauri-apps/api/core";
import { SearchCardLocation } from "./components/search-card/SearchCardLocation"; import { ItemModal } from "./components/item-modal/ItemModal";
import { SearchCardNote } from "./components/search-card/SearchCardNote"; import type { Shortcut } from "./components/shortcuts/hooks/useShortcutEditor";
import { type UserImage, getUserImages } from "./network"; import { type UserImage, getUserImages } from "./network";
import { getCardSize } from "./utils/getCardSize";
// How wonderfully functional // How wonderfully functional
const getAllValues = (object: object): Array<string> => { const getAllValues = (object: object): Array<string> => {
@ -110,6 +109,22 @@ export const Search = () => {
}); });
}); });
const [shortcut, setShortcut] = createSignal<Shortcut>([]);
async function getCurrentShortcut() {
try {
const res: string = await invoke("get_current_shortcut");
console.log("DBG: ", res);
setShortcut(res?.split("+"));
} catch (err) {
console.error("Failed to fetch shortcut:", err);
}
}
onMount(() => {
getCurrentShortcut();
});
return ( return (
<> <>
<main class="container pt-2"> <main class="container pt-2">
@ -174,9 +189,21 @@ export const Search = () => {
</div> </div>
<div class="w-full border-t h-10 bg-white px-4 flex items-center border-neutral-100"> <div class="w-full border-t h-10 bg-white px-4 flex items-center border-neutral-100">
footer <p class="text-sm text-neutral-700">
Use{" "}
{shortcut().length > 0
? shortcut().join("+")
: "shortcut"}{" "}
globally to toggle and reload this window
</p>
</div> </div>
</main> </main>
{selectedItem() && (
<ItemModal
item={selectedItem() as UserImage}
onClose={() => setSelectedItem(null)}
/>
)}
</> </>
); );
}; };

View File

@ -1,7 +1,25 @@
export const ItemModal = () => { import { IconX } from "@tabler/icons-solidjs";
import type { UserImage } from "../../network";
type Props = {
item: UserImage;
onClose: () => void;
};
export const ItemModal = (props: Props) => {
return ( return (
<div class="fixed inset-4 bg-white border border-neutral-300"> <div class="fixed inset-2 rounded-2xl p-4 bg-white border border-neutral-300">
ItemModal <div class="flex justify-between">
<h1 class="text-2xl font-bold">{props.item.data.Name}</h1>
<button type="button" onClick={props.onClose}>
<IconX size={24} class="text-neutral-500" />
</button>
</div>
<div class="flex flex-col gap-2 mb-2">
<p class="text-sm text-neutral-500">
{JSON.stringify(props.item.data, null, 2)}
</p>
</div>
</div> </div>
); );
}; };

View File

@ -12,13 +12,15 @@ export const SearchCardContact = ({ item }: Props) => {
return ( return (
<div class="absolute inset-0 p-3 bg-orange-50"> <div class="absolute inset-0 p-3 bg-orange-50">
<div class="grid grid-cols-[auto_20px] gap-1 mb-1"> <div class="flex mb-1 items-center gap-1">
<p class="text-sm text-neutral-900 font-bold">{data.Name}</p> <IconUser size={14} class="text-neutral-500" />
<IconUser size={20} class="text-neutral-500 mt-1" /> <p class="text-xs text-neutral-500">Contact</p>
</div> </div>
<p class="text-xs text-neutral-500">{data.PhoneNumber}</p> <p class="text-sm text-neutral-900 font-bold mb-1">
<Separator class="my-2" /> {data.Name.length > 0 ? data.Name : "Unknown 🐞"}
<p class="text-xs text-neutral-500">{data.Email}</p> </p>
<p class="text-xs text-neutral-700">Phone: {data.PhoneNumber}</p>
<p class="text-xs text-neutral-700">Mail: {data.Email}</p>
</div> </div>
); );
}; };

View File

@ -10,11 +10,14 @@ export const SearchCardEvent = ({ item }: Props) => {
return ( return (
<div class="absolute inset-0 p-3 bg-purple-50"> <div class="absolute inset-0 p-3 bg-purple-50">
<div class="grid grid-cols-[auto_20px] gap-1 mb-1"> <div class="flex mb-1 items-center gap-1">
<p class="text-sm text-neutral-900 font-bold">{data.Name}</p> <IconCalendar size={14} class="text-neutral-500" />
<IconCalendar size={20} class="text-neutral-500 mt-1" /> <p class="text-xs text-neutral-500">Event</p>
</div> </div>
<p class="text-xs text-neutral-500"> <p class="text-sm text-neutral-900 font-bold mb-1">
{data.Name.length > 0 ? data.Name : "Unknown 🐞"}
</p>
<p class="text-xs text-neutral-700">
Organized by {data.Organizer?.Name ?? "unknown"} on{" "} Organized by {data.Organizer?.Name ?? "unknown"} on{" "}
{data.StartDateTime {data.StartDateTime
? new Date(data.StartDateTime).toLocaleDateString("en-US", { ? new Date(data.StartDateTime).toLocaleDateString("en-US", {

View File

@ -10,11 +10,14 @@ export const SearchCardLocation = ({ item }: Props) => {
return ( return (
<div class="absolute inset-0 p-3 bg-red-50"> <div class="absolute inset-0 p-3 bg-red-50">
<div class="grid grid-cols-[auto_20px] gap-1 mb-1"> <div class="flex mb-1 items-center gap-1">
<p class="text-sm text-neutral-900 font-bold">{data.Name}</p> <IconMapPin size={14} class="text-neutral-500" />
<IconMapPin size={20} class="text-neutral-500 mt-1" /> <p class="text-xs text-neutral-500">Location</p>
</div> </div>
<p class="text-xs text-neutral-500">{data.Address}</p> <p class="text-sm text-neutral-900 font-bold mb-1">
{data.Name.length > 0 ? data.Name : "Unknown 🐞"}
</p>
<p class="text-xs text-neutral-700">Address: {data.Address}</p>
</div> </div>
); );
}; };

View File

@ -1,4 +1,5 @@
import { Separator } from "@kobalte/core/separator"; import { Separator } from "@kobalte/core/separator";
import SolidjsMarkdown from "solidjs-markdown";
import { IconNote } from "@tabler/icons-solidjs"; import { IconNote } from "@tabler/icons-solidjs";
import type { UserImage } from "../../network"; import type { UserImage } from "../../network";
@ -12,11 +13,16 @@ export const SearchCardNote = ({ item }: Props) => {
return ( return (
<div class="absolute inset-0 p-3 bg-green-50"> <div class="absolute inset-0 p-3 bg-green-50">
<div class="grid grid-cols-[auto_20px] gap-1 mb-1"> <div class="flex mb-1 items-center gap-1">
<p class="text-sm text-neutral-900 font-bold">{data.Name}</p> <IconNote size={14} class="text-neutral-500" />
<IconNote size={20} class="text-neutral-500 mt-1" />
</div>
<p class="text-xs text-neutral-500">Note</p> <p class="text-xs text-neutral-500">Note</p>
</div> </div>
<p class="text-sm text-neutral-900 font-bold mb-1">
{data.Name.length > 0 ? data.Name : "Unknown 🐞"}
</p>
<p class="text-xs text-neutral-700">
<SolidjsMarkdown>{data.Content}</SolidjsMarkdown>
</p>
</div>
); );
}; };