reverting back to old searchbar code
This commit is contained in:
parent
697b33a110
commit
30dec5a0cf
@ -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 auto‑select 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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user