add dropdown inputs
This commit is contained in:
parent
9fb697f4ef
commit
13391b18eb
128
src/components/DropdownInput/index.tsx
Normal file
128
src/components/DropdownInput/index.tsx
Normal file
@ -0,0 +1,128 @@
|
||||
import { ChangeEvent, useEffect, useRef, useState } from "react";
|
||||
import "./style.css";
|
||||
|
||||
const Icon = () => {
|
||||
return (
|
||||
<svg height="20" width="20" viewBox="0 0 20 20" fill={"white"}>
|
||||
<path d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
interface dropdownProps {
|
||||
placeholder: string,
|
||||
options: Array<any>,
|
||||
labelPropsName: string,
|
||||
isSearchable: boolean,
|
||||
onChange: Function
|
||||
}
|
||||
|
||||
const DropdownInput = ({
|
||||
placeholder,
|
||||
options,
|
||||
isSearchable,
|
||||
labelPropsName,
|
||||
onChange
|
||||
}: dropdownProps) => {
|
||||
const [showMenu, setShowMenu] = useState<boolean>(false);
|
||||
const [selectedValue, setSelectedValue] = useState<any>();
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const searchRef = useRef<any>();
|
||||
const inputRef = useRef<any>();
|
||||
|
||||
useEffect(() => {
|
||||
setSearchValue("");
|
||||
if (showMenu && searchRef.current) {
|
||||
searchRef.current.focus();
|
||||
}
|
||||
}, [showMenu]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: any) => {
|
||||
if (inputRef.current && !inputRef.current.contains(e.target)) {
|
||||
setShowMenu(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("click", handler);
|
||||
return () => {
|
||||
window.removeEventListener("click", handler);
|
||||
};
|
||||
});
|
||||
const handleInputClick = () => {
|
||||
setShowMenu(!showMenu);
|
||||
};
|
||||
|
||||
const getDisplay = () => {
|
||||
if (!selectedValue) {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
return selectedValue[labelPropsName]
|
||||
};
|
||||
|
||||
const onItemClick = (option: any) => {
|
||||
setSelectedValue(option)
|
||||
onChange(option);
|
||||
};
|
||||
|
||||
const isSelected = (option: any) => {
|
||||
|
||||
if (!selectedValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return selectedValue[labelPropsName] === option[labelPropsName];
|
||||
};
|
||||
|
||||
const onSearch = (e: ChangeEvent) => {
|
||||
const event = e.target as HTMLInputElement
|
||||
setSearchValue(event.value);
|
||||
};
|
||||
|
||||
const getIDropdownInputPropss = () => {
|
||||
if (!searchValue) {
|
||||
return options;
|
||||
}
|
||||
|
||||
return options.filter(
|
||||
(option: any) =>
|
||||
option[labelPropsName].toLowerCase().indexOf(searchValue.toLowerCase()) >= 0
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="dropdown-container">
|
||||
<div ref={inputRef} onClick={handleInputClick} className="dropdown-input">
|
||||
<div className="dropdown-selected-value text-sm">{getDisplay()}</div>
|
||||
<div className="dropdown-tools">
|
||||
<div className="dropdown-tool">
|
||||
<Icon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showMenu && (
|
||||
<div className="dropdown-item-menu">
|
||||
{isSearchable && (
|
||||
<div className="search-box bg-secondary">
|
||||
<input className={'bg-primary text-sm'} onChange={onSearch} value={searchValue} ref={searchRef} />
|
||||
</div>
|
||||
)}
|
||||
<div class={'dropdown-item-container'}>
|
||||
{getIDropdownInputPropss().map((option: any) => (
|
||||
<div
|
||||
onClick={() => onItemClick(option)}
|
||||
key={option[labelPropsName]}
|
||||
className={`dropdown-item text-sm ${isSelected(option) && "selected"}`}
|
||||
>
|
||||
{option[labelPropsName]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownInput;
|
104
src/components/DropdownInput/style.css
Normal file
104
src/components/DropdownInput/style.css
Normal file
@ -0,0 +1,104 @@
|
||||
.dropdown-container {
|
||||
text-align: left;
|
||||
border: 1px solid #474747;
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
min-width: 325px;
|
||||
max-width: 450px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-input {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.dropdown-input-menu {
|
||||
display: block;
|
||||
position: absolute;
|
||||
transform: translateY(4px);
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
overflow: auto;
|
||||
max-height: 150px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dropdown-item-container {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/* width */
|
||||
.dropdown-item-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
.dropdown-item-container::-webkit-scrollbar-track {
|
||||
background: #212121;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
.dropdown-item-container::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
.dropdown-item-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
|
||||
.dropdown-item {
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: #9fc3f870;
|
||||
}
|
||||
|
||||
.dropdown-item.selected {
|
||||
background-color: #a8adb3;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dropdown-selected-value {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.dropdown-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.dropdown-tag-item {
|
||||
background-color: #ddd;
|
||||
padding: 2px 4px;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dropdown-tag-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 5px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
}
|
@ -3,6 +3,7 @@ import SeparatorWithAnchor from "./Separator/WithAnchor";
|
||||
import DefaultSeparator from "./Separator/Default";
|
||||
import Footer from './Footer/';
|
||||
import CustomInterweave from "./CustomInterweave";
|
||||
import DropdownInput from "./DropdownInput";
|
||||
|
||||
import SpinnerLoading from "./Loading/Spinner";
|
||||
|
||||
@ -12,6 +13,7 @@ export {
|
||||
DefaultSeparator,
|
||||
Footer,
|
||||
CustomInterweave,
|
||||
DropdownInput,
|
||||
|
||||
SpinnerLoading,
|
||||
}
|
Loading…
Reference in New Issue
Block a user