reverting back to old searchbar code

This commit is contained in:
MuslemRahimi 2025-03-08 09:35:52 +01:00
parent 697b33a110
commit 30dec5a0cf

View File

@ -4,7 +4,6 @@
import Search from "lucide-svelte/icons/search"; import Search from "lucide-svelte/icons/search";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { Combobox } from "bits-ui"; import { Combobox } from "bits-ui";
import { tick } from "svelte";
let searchHistory = []; let searchHistory = [];
let updatedSearchHistory = []; let updatedSearchHistory = [];
@ -15,24 +14,39 @@
let showSuggestions = false; let showSuggestions = false;
let touchedInput = false; let touchedInput = false;
// FIX: Declare inputValue as a normal variable instead of using a reactive assignment. $: inputValue = "";
let inputValue = "";
let nextPage = false; let nextPage = false;
let searchOpen = false; let searchOpen = false;
let searchBarModalChecked = false; // Initialize it to false let searchBarModalChecked = false; // Initialize it to false
let inputElement; let inputElement;
let isNavigating = false; let isNavigating = false;
// NEW: Flag to ensure we only auto-dispatch ArrowDown once per modal open.
let suggestionSelected = false;
const popularList = [ const popularList = [
{ symbol: "KO", name: "Coca Cola Company", type: "Stock" }, {
{ symbol: "TSLA", name: "Tesla Inc", type: "Stock" }, symbol: "KO",
{ symbol: "AMD", name: "Advanced Micro Devices", type: "Stock" }, name: "Coca Cola Company",
{ symbol: "SPY", name: "SPDR S&P 500 ETF Trust", type: "ETF" }, type: "Stock",
{ symbol: "NVDA", name: "Nvidia", type: "Stock" }, },
{
symbol: "TSLA",
name: "Tesla Inc",
type: "Stock",
},
{
symbol: "AMD",
name: "Advanced Micro Devices",
type: "Stock",
},
{
symbol: "SPY",
name: "SPDR S&P 500 ETF Trust",
type: "ETF",
},
{
symbol: "NVDA",
name: "Nvidia",
type: "Stock",
},
]; ];
async function handleSearch(symbol, assetType) { async function handleSearch(symbol, assetType) {
@ -59,7 +73,7 @@
closePopup?.dispatchEvent(new MouseEvent("click")); closePopup?.dispatchEvent(new MouseEvent("click"));
} }
goto( await goto(
`/${assetType === "ETF" ? "etf" : assetType === "Index" ? "index" : "stocks"}/${symbol}`, `/${assetType === "ETF" ? "etf" : assetType === "Index" ? "index" : "stocks"}/${symbol}`,
); );
@ -96,6 +110,7 @@
const closePopup = document.getElementById("searchBarModal"); const closePopup = document.getElementById("searchBarModal");
closePopup?.dispatchEvent(new MouseEvent("click")); closePopup?.dispatchEvent(new MouseEvent("click"));
// Reset the flag after a short delay
setTimeout(() => { setTimeout(() => {
isNavigating = false; isNavigating = false;
}, 100); }, 100);
@ -139,11 +154,11 @@
async function search() { async function search() {
isLoading = true; isLoading = true;
clearTimeout(timeoutId); // Clear any existing timeout
clearTimeout(timeoutId);
if (!inputValue.trim()) { if (!inputValue.trim()) {
searchBarData = []; // Skip if query is empty or just whitespace
searchBarData = []; // Clear previous results
isLoading = false; isLoading = false;
return; return;
} }
@ -153,8 +168,8 @@
`/api/searchbar?query=${encodeURIComponent(inputValue)}&limit=10`, `/api/searchbar?query=${encodeURIComponent(inputValue)}&limit=10`,
); );
searchBarData = await response?.json(); searchBarData = await response?.json();
isLoading = false; }, 50); // delay
}, 200); isLoading = false;
} }
function handleKeyDown(symbol) { function handleKeyDown(symbol) {
@ -188,6 +203,7 @@
function saveRecentTicker() { function saveRecentTicker() {
try { try {
// Save the version along with the rules
localStorage?.setItem("search-history", JSON?.stringify(searchHistory)); localStorage?.setItem("search-history", JSON?.stringify(searchHistory));
} catch (e) { } catch (e) {
console.log("Failed saving indicator rules: ", e); console.log("Failed saving indicator rules: ", e);
@ -197,12 +213,14 @@
onMount(() => { onMount(() => {
try { try {
const savedRules = localStorage?.getItem("search-history"); const savedRules = localStorage?.getItem("search-history");
if (savedRules) { if (savedRules) {
searchHistory = JSON.parse(savedRules); searchHistory = JSON.parse(savedRules);
} }
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
window.addEventListener("keydown", handleControlK); window.addEventListener("keydown", handleControlK);
return () => { return () => {
window.removeEventListener("keydown", handleControlK); window.removeEventListener("keydown", handleControlK);
@ -211,21 +229,17 @@
$: { $: {
if (searchBarModalChecked === true && typeof window !== "undefined") { if (searchBarModalChecked === true && typeof window !== "undefined") {
console.log("open");
if ($screenWidth > 640) { if ($screenWidth > 640) {
inputElement.focus(); inputElement.focus();
} }
// Prevent body scroll while modal is open //Page is not scrollable now
document.body.classList.add("overflow-hidden"); document.body.classList.add("overflow-hidden");
// Reset our flag when modal is (re)opened.
suggestionSelected = false;
} }
} }
$: { $: {
if (searchBarModalChecked === false && typeof window !== "undefined") { if (searchBarModalChecked === false && typeof window !== "undefined") {
showSuggestions = inputValue = ""; showSuggestions = inputValue = "";
suggestionSelected = false;
document.body.classList?.remove("overflow-hidden"); document.body.classList?.remove("overflow-hidden");
} }
} }
@ -236,8 +250,10 @@
updatedSearchHistory?.length > 0 updatedSearchHistory?.length > 0
) { ) {
(async () => { (async () => {
// Delay is needed so that the #each block updates properly // Add 500 ms delay is important otherwise bug since #each has searchHistory and updates too quickly and redirects to wrong symbol
await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500));
// Update search history after delay
searchHistory = updatedSearchHistory; searchHistory = updatedSearchHistory;
updatedSearchHistory = []; updatedSearchHistory = [];
saveRecentTicker(); saveRecentTicker();
@ -248,7 +264,11 @@
$: { $: {
if (searchBarData) { if (searchBarData) {
showSuggestions = searchBarData?.length > 0; if (searchBarData?.length > 0) {
showSuggestions = true;
} else {
showSuggestions = false;
}
} }
} }
@ -257,24 +277,6 @@
search(); search();
} }
} }
// FIX: Dispatch ArrowDown only once (when suggestions open) to autoselect the first suggestion.
$: if (
showSuggestions &&
searchBarData?.length &&
touchedInput &&
!suggestionSelected
) {
tick().then(() => {
const input = document.getElementById("combobox-input");
if (input) {
input.dispatchEvent(
new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
);
suggestionSelected = true;
}
});
}
</script> </script>
<div class="hidden sm:block w-full sm:max-w-[600px]"> <div class="hidden sm:block w-full sm:max-w-[600px]">
@ -328,13 +330,7 @@
<div <div
class="absolute inset-y-0 right-0 flex items-center gap-x-2 px-3 text-gray-350 font-semibold" class="absolute inset-y-0 right-0 flex items-center gap-x-2 px-3 text-gray-350 font-semibold"
> >
{#if isLoading && inputValue?.length > 0} {#if isLoading}
<div
class="pointer-events-none absolute end-6 top-2.5 gap-1 opacity-80 rtl:flex-row-reverse flex"
>
<span class="loading loading-spinner loading-sm"></span>
</div>
{:else if isLoading}
<span class="loading loading-spinner loading-sm"></span> <span class="loading loading-spinner loading-sm"></span>
{:else if inputValue?.length > 0} {:else if inputValue?.length > 0}
<label class="cursor-pointer" on:click={() => (inputValue = "")}> <label class="cursor-pointer" on:click={() => (inputValue = "")}>
@ -358,79 +354,56 @@
{/if} {/if}
</div> </div>
</div> </div>
{#if isLoading && inputValue?.length > 0}{:else} <Combobox.Content
<Combobox.Content class="w-auto z-40 -mt-0.5 rounded-md border border-gray-700 bg-secondary px-1 py-3 shadow-popover outline-hidden"
class="w-auto z-40 -mt-0.5 rounded-md border border-gray-700 bg-secondary px-1 py-3 shadow-popover outline-hidden" sideOffset={8}
sideOffset={8} >
> {#if inputValue?.length > 0 && searchBarData?.length > 0}
{#if inputValue?.length > 0 && searchBarData?.length > 0} <div
<div class="pl-2 pb-2 border-b border-gray-600 text-white text-sm font-semibold w-full"
class="pl-2 pb-2 border-b border-gray-600 text-white text-sm font-semibold w-full" >
Suggestions
</div>
{#each searchBarData as item}
<Combobox.Item
class="cursor-pointer text-white border-b border-gray-600 last:border-none flex h-fit w-auto select-none items-center rounded-button py-3 pl-2 pr-1.5 text-sm capitalize outline-hidden transition-all duration-75 data-highlighted:bg-primary"
value={item?.symbol}
label={item?.name}
on:click={() => handleSearch(item?.symbol, item?.type)}
> >
Suggestions <div class="flex flex-row items-center justify-between w-full">
</div> <span class="text-sm text-blue-400">{item?.symbol}</span>
{#each searchBarData as item} <span class="ml-3 text-sm text-white">{item?.name}</span>
<Combobox.Item <span class="ml-auto text-sm text-white">{item?.type}</span>
class="cursor-pointer text-white border-b border-gray-600 last:border-none flex h-fit w-auto select-none items-center rounded-button py-3 pl-2 pr-1.5 text-sm capitalize outline-hidden transition-all duration-75 data-highlighted:bg-primary" </div>
value={item?.symbol} </Combobox.Item>
label={item?.name} {/each}
on:click={() => handleSearch(item?.symbol, item?.type)} {:else if inputValue?.length === 0 || !showSuggestions}
> <div
<div class="pl-2 pb-2 border-b border-gray-600 text-white text-sm font-semibold w-full"
class="flex flex-row items-center justify-between w-full" >
> {searchHistory?.length > 0 ? "Recent" : "Popular"}
<span class="text-sm text-blue-400">{item?.symbol}</span> </div>
<span class="ml-3 text-sm text-white">{item?.name}</span> {#each searchHistory?.length > 0 ? searchHistory : popularList as item}
<span class="ml-auto text-sm text-white">{item?.type}</span> <Combobox.Item
</div> class="cursor-pointer text-white border-b border-gray-600 last:border-none flex h-fit w-auto select-none items-center rounded-button py-3 pl-2 pr-1.5 text-sm capitalize outline-hidden transition-all duration-75 data-highlighted:bg-primary"
</Combobox.Item> value={item?.symbol}
{/each} label={item?.name}
{:else if inputValue?.length === 0} on:click={() => handleSearch(item?.symbol, item?.type)}
<div
class="pl-2 pb-2 border-b border-gray-600 text-white text-sm font-semibold w-full"
> >
{searchHistory?.length > 0 ? "Recent" : "Popular"} <div class="flex flex-row items-center justify-between w-full">
</div> <span class="text-sm text-blue-400">{item?.symbol}</span>
{#each searchHistory as item} <span class="ml-3 text-sm text-white">{item?.name}</span>
<Combobox.Item <span class="ml-auto text-sm text-white">{item?.type}</span>
class="cursor-pointer text-white border-b border-gray-600 last:border-none flex h-fit w-auto select-none items-center rounded-button py-3 pl-2 pr-1.5 text-sm capitalize outline-hidden transition-all duration-75 data-highlighted:bg-primary" </div>
value={item?.symbol} </Combobox.Item>
label={item?.name} {/each}
on:click={() => handleSearch(item?.symbol, item?.type)} {:else}
> <span class="block px-5 py-2 text-sm text-white">
<div No results found
class="flex flex-row items-center justify-between w-full" </span>
> {/if}
<span class="text-sm text-blue-400">{item?.symbol}</span> </Combobox.Content>
<span class="ml-3 text-sm text-white">{item?.name}</span>
<span class="ml-auto text-sm text-white">{item?.type}</span>
</div>
</Combobox.Item>
{/each}
{:else if showSuggestions}
{#each searchHistory as item}
<Combobox.Item
class="cursor-pointer text-white border-b border-gray-600 last:border-none flex h-fit w-auto select-none items-center rounded-button py-3 pl-2 pr-1.5 text-sm capitalize outline-hidden transition-all duration-75 data-highlighted:bg-primary"
value={item?.symbol}
label={item?.name}
on:click={() => handleSearch(item?.symbol, item?.type)}
>
<div
class="flex flex-row items-center justify-between w-full"
>
<span class="text-sm text-blue-400">{item?.symbol}</span>
<span class="ml-3 text-sm text-white">{item?.name}</span>
<span class="ml-auto text-sm text-white">{item?.type}</span>
</div>
</Combobox.Item>
{/each}
{:else}
<span class="block px-5 py-2 text-sm text-white">
No results found
</span>
{/if}
</Combobox.Content>
{/if}
</Combobox.Root> </Combobox.Root>
</div> </div>
</div> </div>
@ -454,7 +427,7 @@
<label for="searchBarModal" class="cursor-pointer modal-backdrop"></label> <label for="searchBarModal" class="cursor-pointer modal-backdrop"></label>
<div <div
class="z-40 modal-box overflow-hidden rounded-md bg-secondary border border-gray-600 sm:my-8 sm:m-auto sm:h-auto w-full sm:w-3/4 lg:w-1/2 2xl:w-1/3" class="z-999 modal-box overflow-hidden rounded-md bg-secondary border border-gray-600 sm:my-8 sm:m-auto sm:h-auto w-full sm:w-3/4 lg:w-1/2 2xl:w-1/3"
> >
<label <label
for="searchBarModal" for="searchBarModal"