From 13391b18eba5a495271bb907928311ad452cc649 Mon Sep 17 00:00:00 2001 From: NCanggoro Date: Tue, 3 Oct 2023 14:38:49 +0700 Subject: [PATCH] add dropdown inputs --- src/components/DropdownInput/index.tsx | 128 +++++++++++++++++++++++++ src/components/DropdownInput/style.css | 104 ++++++++++++++++++++ src/components/index.ts | 2 + 3 files changed, 234 insertions(+) create mode 100644 src/components/DropdownInput/index.tsx create mode 100644 src/components/DropdownInput/style.css diff --git a/src/components/DropdownInput/index.tsx b/src/components/DropdownInput/index.tsx new file mode 100644 index 0000000..657e0be --- /dev/null +++ b/src/components/DropdownInput/index.tsx @@ -0,0 +1,128 @@ +import { ChangeEvent, useEffect, useRef, useState } from "react"; +import "./style.css"; + +const Icon = () => { + return ( + + + + ); +}; + +interface dropdownProps { + placeholder: string, + options: Array, + labelPropsName: string, + isSearchable: boolean, + onChange: Function +} + +const DropdownInput = ({ + placeholder, + options, + isSearchable, + labelPropsName, + onChange +}: dropdownProps) => { + const [showMenu, setShowMenu] = useState(false); + const [selectedValue, setSelectedValue] = useState(); + const [searchValue, setSearchValue] = useState(""); + const searchRef = useRef(); + const inputRef = useRef(); + + 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 ( +
+
+
{getDisplay()}
+
+
+ +
+
+
+ {showMenu && ( +
+ {isSearchable && ( +
+ +
+ )} +
+ {getIDropdownInputPropss().map((option: any) => ( +
onItemClick(option)} + key={option[labelPropsName]} + className={`dropdown-item text-sm ${isSelected(option) && "selected"}`} + > + {option[labelPropsName]} +
+ ))} +
+
+ )} +
+ ); +}; + +export default DropdownInput; diff --git a/src/components/DropdownInput/style.css b/src/components/DropdownInput/style.css new file mode 100644 index 0000000..2559627 --- /dev/null +++ b/src/components/DropdownInput/style.css @@ -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; +} \ No newline at end of file diff --git a/src/components/index.ts b/src/components/index.ts index 71ea9d4..82ab401 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -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, } \ No newline at end of file