Skip to content

Commit

Permalink
Merge pull request #236 from DeXter-on-Radix/pair-selector-imporvements
Browse files Browse the repository at this point in the history
Pair selector imporvements
  • Loading branch information
EvgeniiaVak authored Jan 28, 2024
2 parents d4b250e + 9729953 commit d9643cc
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 91 deletions.
226 changes: 136 additions & 90 deletions src/app/components/PairSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useAppSelector, useAppDispatch } from "../hooks";
import { selectPairAddress } from "../state/pairSelectorSlice";
import { orderInputSlice } from "../state/orderInputSlice";
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { FaSearch } from "react-icons/fa";

interface PairInfo {
name: string;
Expand All @@ -14,8 +15,22 @@ function displayName(name?: string) {
export function PairSelector() {
const pairSelector = useAppSelector((state) => state.pairSelector);
const dispatch = useAppDispatch();
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(0);
const [filteredOptions, setFilteredOptions] = useState<PairInfo[]>([]);

const inputRef = useRef<HTMLInputElement>(null);

const options = pairSelector.pairsList;
// uncomment duplicates to test scroll behavior
const options = useMemo(
() => [
// ...pairSelector.pairsList,
// ...pairSelector.pairsList,
...pairSelector.pairsList,
],
[pairSelector.pairsList]
);
const id = "pairOption";

useEffect(() => {
Expand All @@ -26,110 +41,141 @@ export function PairSelector() {
}
}, [dispatch]);

const handleChange = (val: PairInfo | null) => {
if (val == null) return;

dispatch(orderInputSlice.actions.resetNumbersInput());

const pairAddress = val["address"];
dispatch(selectPairAddress(pairAddress));
};

const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);

const inputRef = useRef(null);
const dropdownRef = useRef(null);

const selectOption = (option: PairInfo) => {
const selectOption = useCallback(() => {
const option = filteredOptions[highlightedIndex];
setQuery(() => "");
handleChange(option);
dispatch(orderInputSlice.actions.resetNumbersInput());
dispatch(selectPairAddress(option["address"]));
setIsOpen((isOpen) => !isOpen);
};

function toggle(e: React.MouseEvent<HTMLInputElement>) {
setIsOpen(e && e.target === inputRef.current);
}
}, [dispatch, highlightedIndex, filteredOptions]);

const getDisplayValue = () => {
if (query) return displayName(query);
if (isOpen) return displayName(query);
if (pairSelector.name) return displayName(pairSelector.name);

return "";
};

const filter = (options: PairInfo[]) => {
return options.filter(
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === "/") {
e.preventDefault();
setIsOpen(true);
} else if (e.key === "Escape") {
setQuery("");
setIsOpen(false);
} else if (e.key === "Enter" && isOpen) {
e.preventDefault();
selectOption();
} else if (e.key === "ArrowDown" && isOpen) {
e.preventDefault();
setHighlightedIndex((highlightedIndex) =>
highlightedIndex < filteredOptions.length - 1
? highlightedIndex + 1
: 0
);
} else if (e.key === "ArrowUp" && isOpen) {
e.preventDefault();
setHighlightedIndex((highlightedIndex) =>
highlightedIndex > 0
? highlightedIndex - 1
: filteredOptions.length - 1
);
}
},
[isOpen, selectOption, filteredOptions.length]
);

useEffect(() => {
window.addEventListener("keydown", handleKeyDown);

// Remove the event listener when the component unmounts
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [handleKeyDown]);

useEffect(() => {
if (isOpen) {
inputRef.current?.focus();
inputRef.current?.select();
} else {
inputRef.current?.blur();
}
}, [isOpen]);

useEffect(() => {
const newOptions = options.filter(
(option) => option["name"].toLowerCase().indexOf(query.toLowerCase()) > -1
);
};
setFilteredOptions(newOptions);
setHighlightedIndex(0);
}, [options, query]);

return (
<div className="w-full">
<div className="dropdown dropdown-end w-full">
<label className="btn btn-block text-xl font-bold no-animation">
<div className="flex justify-between selected-value w-full">
<input
id="pair-selector-text"
ref={inputRef}
type="text"
value={getDisplayValue()}
name="searchTerm"
onChange={(e) => {
setQuery(e.target.value);
handleChange(null);
}}
onClick={toggle}
className="flex-initial !bg-transparent"
/>
<span className="flex-none order-last pt-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="white"
viewBox="0 0 16 16"
>
<path d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
</svg>
</span>
</div>
</label>
<ul
ref={dropdownRef}
tabIndex={0}
className={
`options ${isOpen ? "" : "hidden"}` +
" dropdown-content z-10 menu bg-base-200 shadow rounded-box w-full !my-0 !p-0"
}
>
{filter(options).map((option, index) => {
return (
<li
onClick={() => selectOption(option)}
className=" font-bold !pl-0"
key={`${id}-${index}`}
>
<div className="flex justify-between">
<span className="flex-1">{displayName(option["name"])}</span>
<span className="flex-none">+</span>
</div>
</li>
);
})}
<li>
<div
<div
className={
"w-full h-full relative uppercase" + (isOpen ? " bg-base-100" : "")
}
>
<div
className="w-full h-full flex text-xl font-bold justify-between p-4"
onClick={() => {
setIsOpen((isOpen) => !isOpen);
}}
>
<input
id="pair-selector-text"
ref={inputRef}
type="text"
value={getDisplayValue()}
name="searchTerm"
onChange={(e) => {
setQuery(e.target.value);
}}
className="flex-initial !bg-transparent uppercase"
/>
<div className="flex space-x-2 text-secondary-content">
<FaSearch className="my-auto" />
<span className="px-2 bg-neutral !rounded-sm text-neutral-content my-auto">
/
</span>
</div>
</div>
<ul
tabIndex={0}
className={
`${isOpen ? "" : "hidden"}` +
" absolute z-30 bg-base-100 w-full !my-0 !p-0 overflow-auto max-h-screen"
}
>
{filteredOptions.map((option, index) => {
return (
<li
onMouseEnter={() => setHighlightedIndex(index)}
onClick={() => selectOption()}
className={
filter(options).length == 0
? "flex justify-between"
: "flex justify-between hidden"
"font-bold !px-4 py-0 cursor-pointer" +
(highlightedIndex === index ? " bg-base-300" : "")
}
key={`${id}-${index}`}
>
<span className="flex-1">No Results</span>
</div>
</li>
</ul>
</div>
<div className="flex justify-between ">
<span className="">{displayName(option["name"])}</span>
<span className="">+</span>
</div>
</li>
);
})}
<li
className={
"justify-between " +
(filteredOptions.length == 0 ? "flex" : "hidden")
}
>
<span className="flex-1">No Results</span>
</li>
</ul>
</div>
);
}
2 changes: 1 addition & 1 deletion src/app/components/PriceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function PriceChartCanvas(props: PriceChartProps) {
<div
ref={legendRef}
className={
"absolute font-bold text-xs text-left text-secondary-content mt-3 z-50 uppercase " +
"absolute font-bold text-xs text-left text-secondary-content mt-3 z-20 uppercase " +
(props.isNegativeOrZero ? "!text-error" : "!text-success")
}
>
Expand Down

0 comments on commit d9643cc

Please sign in to comment.