Skip to content

Commit 7f51e60

Browse files
committed
client(SelectInput.tsx): create a brand new, fully-featured custom select input with the ability to filter the items
1 parent 7733779 commit 7f51e60

14 files changed

+183
-61
lines changed

client/build/asset-manifest.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/build/index.html

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/build/static/css/main.3b48ac6d.css

Lines changed: 0 additions & 4 deletions
This file was deleted.

client/build/static/css/main.3b48ac6d.css.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

client/build/static/css/main.fccc1dae.css

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/build/static/css/main.fccc1dae.css.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/build/static/js/main.08b83b3c.js renamed to client/build/static/js/main.72137d1c.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/build/static/js/main.08b83b3c.js.map renamed to client/build/static/js/main.72137d1c.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/src/components/input/SelectInput.tsx

Lines changed: 110 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,129 @@
1-
import { FC, useId } from "react";
1+
import { FC, useEffect, useState } from "react";
2+
import { FiCheck } from "react-icons/fi";
3+
import { useOuterClick } from "../../hooks/useOuterClick";
24

35
interface SelectInputProps {
46
label: string;
57
setValue: (value: string) => void;
68
options: string[];
79
value: string;
810
className?: string;
11+
searchField?: boolean;
912
}
1013

1114
const SelectInput: FC<SelectInputProps> = (props) => {
12-
const id = `${props.label}-${useId()}`;
15+
const [isActive, setIsActive] = useState<boolean>(false);
16+
const [filterValue, setFilterValue] = useState<string>("");
17+
const [options, setOptions] = useState<string[]>([...props.options]);
18+
19+
const ref = useOuterClick(() => setIsActive(false));
20+
21+
useEffect(() => {
22+
if (filterValue.trim() === "") {
23+
setOptions([...props.options]);
24+
return;
25+
}
26+
27+
setOptions(
28+
[...props.options].filter((o) =>
29+
o.includes(filterValue.toLowerCase().trim())
30+
)
31+
);
32+
}, [filterValue, props.options]);
33+
34+
const selectValue = (value: string): void => {
35+
setIsActive(false);
36+
setFilterValue("");
37+
props.setValue(value);
38+
};
1339

1440
return (
15-
<div className={`${props.className} w-full flex flex-col gap-1`}>
16-
<label
17-
htmlFor={id}
18-
className="text-sm text-gh-text-secondary font-semibold w-fit"
19-
>
41+
<div
42+
className={`${props.className} w-full h-fit select-none relative`}
43+
ref={ref}
44+
>
45+
<label className="text-sm text-gh-text-secondary font-semibold w-fit block mb-1">
2046
{props.label}
2147
</label>
2248

23-
<select
24-
name={id}
25-
id={id}
26-
value={props.value}
27-
autoComplete="off"
28-
onChange={(e) => props.setValue(e.target.value)}
29-
className="ml-auto text-base bg-gh-bg border border-solid border-gh-border
30-
rounded-md px-2 py-1 leading-none placeholder:text-gh-text-secondary text-gh-text
31-
outline-none focus:border-gh-blue active:border-gh-blue transition-all
32-
duration-150 w-full"
49+
<div
50+
onClick={() => setIsActive((prev) => !prev)}
51+
onBlur={() => setIsActive(false)}
52+
className={`ml-auto text-base border border-solid rounded-md px-2
53+
pb-1 pt-[.16rem] text-gh-text transition-all cursor-pointer
54+
duration-150 w-full flex items-center justify-between relative
55+
hover:border-gh-button-border-active hover:bg-gh-bg-secondary
56+
${
57+
isActive
58+
? "bg-gh-bg-secondary border-gh-button-border-active shadow-2xl"
59+
: "bg-gh-bg border-gh-border"
60+
}`}
3361
>
34-
{props.options.map((option, index) => {
35-
return (
36-
<option value={option} key={`${option}-${index}`}>
37-
{option}
38-
</option>
39-
);
40-
})}
41-
</select>
62+
<div>{props.value}</div>
63+
<div
64+
className="border-[.25em] translate-y-1/4
65+
border-solid border-transparent border-t-gh-text-secondary"
66+
/>
67+
</div>
68+
69+
<div
70+
className={`bg-gh-bg-secondary border border-solid border-gh-border
71+
rounded-md shadow-3xl z-50 absolute w-fit top-16 left-0 text-sm ${
72+
isActive
73+
? "opacity-100 pointer-events-auto"
74+
: "opacity-0 pointer-events-none"
75+
}`}
76+
>
77+
<div
78+
className="text-gh-text font-semibold py-1 px-3 border-b
79+
border-solid border-gh-border"
80+
>
81+
{props.label}
82+
</div>
83+
84+
{props.searchField && (
85+
<div
86+
className="py-1 px-2 border-b
87+
border-solid border-gh-border"
88+
>
89+
<input
90+
placeholder="Filter..."
91+
value={filterValue}
92+
onChange={(e) => setFilterValue(e.target.value)}
93+
className="bg-gh-bg px-2 py-1 text-gh-text placeholder:text-gh-text-secondary
94+
border border-solid border-gh-border rounded-md outline-none
95+
focus:border-gh-blue active:border-gh-blue"
96+
/>
97+
</div>
98+
)}
99+
100+
<ul className="list-none max-h-48 overflow-auto">
101+
{options.map((option, i) => {
102+
const isSelected = option === props.value;
103+
104+
return (
105+
<li
106+
key={option}
107+
onClick={() => selectValue(option)}
108+
className={`flex items-center gap-4 py-1 px-2 transition-all duration-150
109+
hover:bg-gh-button cursor-pointer text-gh-text min-w-[10rem] ${
110+
i === 0 ? "" : "border-t border-solid border-gh-border"
111+
}`}
112+
>
113+
{isSelected && (
114+
<span>
115+
<FiCheck />
116+
</span>
117+
)}
118+
119+
<span className={`${isSelected ? "" : "pl-[1.85rem]"}`}>
120+
{option}
121+
</span>
122+
</li>
123+
);
124+
})}
125+
</ul>
126+
</div>
42127
</div>
43128
);
44129
};

0 commit comments

Comments
 (0)