Skip to content

Commit 9ceda47

Browse files
authored
feat(search): improve keyboard and mouse event handling for better UX (#1846)
* feat(search): improve keyboard and mouse event handling for better user experience - Improved Event Handling Mechanism - Added Event Handling for `handleBackdropClick` and ESC Key - Added Support for Apple macOS Command Key * fix: fix lint
1 parent 8601de9 commit 9ceda47

File tree

1 file changed

+28
-17
lines changed

1 file changed

+28
-17
lines changed

src/lib/components/chat/Search.svelte

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,17 @@
1010
import { base } from "$app/paths";
1111
1212
import { debounce } from "$lib/utils/debounce";
13-
import { onDestroy, onMount } from "svelte";
1413
1514
import type { GETSearchEndpointReturn } from "../../../routes/api/conversations/search/+server";
1615
import NavConversationItem from "../NavConversationItem.svelte";
1716
import { titles } from "../NavMenu.svelte";
1817
import { beforeNavigate } from "$app/navigation";
19-
import { browser } from "$app/environment";
2018
2119
import CarbonClose from "~icons/carbon/close";
2220
import { fly } from "svelte/transition";
2321
import InfiniteScroll from "../InfiniteScroll.svelte";
2422
23+
let searchContainer: HTMLDivElement | undefined = $state(undefined);
2524
let inputElement: HTMLInputElement | undefined = $state(undefined);
2625
2726
let searchInput: string = $state("");
@@ -66,6 +65,15 @@
6665
}
6766
}, 300);
6867
68+
const handleBackdropClick = (event: MouseEvent) => {
69+
if (!searchOpen || !searchContainer) return;
70+
71+
const target = event.target;
72+
if (!(target instanceof Node) || !searchContainer.contains(target)) {
73+
searchOpen = false;
74+
}
75+
};
76+
6977
async function handleVisible(v: string) {
7078
const newConvs = await fetch(`${base}/api/conversations/search?q=${v}&p=${page++}`)
7179
.then(async (r) => {
@@ -91,29 +99,28 @@
9199
92100
$effect(() => update(searchInput));
93101
94-
async function openSearchListener(ev: KeyboardEvent) {
95-
if (ev.ctrlKey && ev.key === "k") {
96-
searchOpen = true;
97-
ev.stopPropagation();
98-
ev.preventDefault();
102+
function handleKeydown(event: KeyboardEvent) {
103+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
104+
if (!searchOpen) {
105+
searchOpen = true;
106+
}
107+
event.preventDefault();
108+
event.stopPropagation();
99109
}
100-
}
101110
102-
onMount(() => {
103-
if (!browser) return;
104-
window.addEventListener("keydown", openSearchListener);
105-
});
111+
if (searchOpen && event.key === "Escape") {
112+
if (searchOpen) {
113+
searchOpen = false;
114+
}
115+
event.preventDefault();
116+
}
117+
}
106118
107119
beforeNavigate(() => {
108120
searchOpen = false;
109121
searchInput = "";
110122
});
111123
112-
onDestroy(() => {
113-
if (!browser) return;
114-
window.removeEventListener("keydown", openSearchListener);
115-
});
116-
117124
$effect(() => {
118125
if (searchOpen) {
119126
inputElement?.focus();
@@ -128,8 +135,11 @@
128135
});
129136
</script>
130137

138+
<svelte:window onkeydown={handleKeydown} onmousedown={handleBackdropClick} />
139+
131140
{#if searchOpen}
132141
<div
142+
bind:this={searchContainer}
133143
class="fixed bottom-0 left-[5%] right-[5%] top-[10%] z-50
134144
m-4 mx-auto h-fit max-w-2xl
135145
overflow-hidden rounded-xl
@@ -151,6 +161,7 @@
151161
type="text"
152162
name="searchbar"
153163
placeholder="Search for chats..."
164+
autocomplete="off"
154165
class={{
155166
"h-12 w-full p-4 text-lg dark:bg-gray-800 dark:text-gray-200": true,
156167
"border-b border-b-gray-500/50": searchInput && searchInput.length >= 3,

0 commit comments

Comments
 (0)