Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ayush/leaderboard search #320

Merged
merged 14 commits into from
Nov 12, 2023
1 change: 1 addition & 0 deletions components/leaderboard/RankCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const RankCard: FunctionComponent<RankCardProps> = ({
width="32"
color={iconMap[position as keyof typeof iconMap]}
/>

</div>

<div className={styles.rank_card_naming}>
Expand Down
103 changes: 90 additions & 13 deletions components/leaderboard/searchbar.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,106 @@
import React, { FunctionComponent } from "react";
import React, {
useContext,
useEffect,
useState,
FunctionComponent,
} from "react";
import SearchIcon from "../../public/icons/searchIcon.svg";
import Image from "next/image";
import styles from "../../styles/leaderboard.module.css";
import CrossIcon from "../../public/icons/cross.svg";
import { StarknetIdJsContext } from "../../context/StarknetIdJsProvider";
import { hexToDecimal } from "../../utils/feltService";

type SearchbarProps = {
handleSearch: (_: string) => void;
handleChange: (_: string) => void;
value: string;
onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;
suggestions: string[];
handleSuggestionClick: (_: string) => void;
};

const Searchbar: FunctionComponent<SearchbarProps> = ({
handleSearch,
handleChange,
value,
onKeyDown,
suggestions,
handleSuggestionClick,
}) => {
const [showSuggestions, setShowSuggestions] = useState(
suggestions?.length > 0
);

const { starknetIdNavigator } = useContext(StarknetIdJsContext);

useEffect(() => {
const handleOutsideClick = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (
target.className !== styles.search_bar_container &&
target.className !== styles.search_bar &&
target.className !== styles.search_bar_suggestions
) {
setShowSuggestions(false);
} else {
setShowSuggestions(true);
}
};

document.addEventListener("click", handleOutsideClick);

return () => {
document.removeEventListener("click", handleOutsideClick);
};
}, []);

const handleOptionClick = async (option: string) => {
const addr = await starknetIdNavigator
?.getAddressFromStarkName(option)
.catch((err) => {
return "";
});
if (!addr) return;
handleChange(option);
handleSuggestionClick(hexToDecimal(addr));
};

return (
<div className="flex flex-row p-2 bg-background w-full rounded-lg">
<Image src={SearchIcon} priority width={16} height={16} />
<input
value={value}
onChange={(e) => handleSearch(e.target.value)}
className="bg-transparent outline-none ml-2 w-full"
placeholder="Search"
style={{ fontSize: 14 }}
onKeyDown={onKeyDown}
/>
<div className="relative gap-2 z-50">
<div className={styles.search_bar_container}>
<Image src={SearchIcon} priority width={16} height={16} />
<input
value={value}
onChange={(e) => handleChange(e.target.value)}
className={styles.search_bar}
placeholder="Search"
style={{ fontSize: 14 }}
onKeyDown={onKeyDown}
/>
{value.length > 0 ? (
<div
className="flex cursor-pointer"
onClick={() => {
handleChange("");
handleSuggestionClick("");
}}
>
<Image src={CrossIcon} priority width={20} height={20} />
</div>
) : null}
</div>
{showSuggestions && suggestions?.length > 0 ? (
<div className={styles.search_bar_suggestions}>
{suggestions.map((suggestion, index) => (
<div
className="flex cursor-pointer"
key={index}
onClick={() => handleOptionClick(suggestion)}
>
<p>{suggestion}</p>
</div>
))}
</div>
) : null}
</div>
);
};
Expand Down
15 changes: 15 additions & 0 deletions hooks/useDebounce.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect, useState } from "react";

export function useDebounce<T>(value: T, delay?: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);

useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay || 500);

return () => {
clearTimeout(timer);
};
}, [value, delay]);

return debouncedValue;
}
Loading