import _ from "lodash"; import React, { useMemo, useState } from "react"; import styled from "styled-components"; import Loading from "./Loading"; type Props = { options: T[]; onSelect: (option: T) => void; label?: string; dropdownLabel?: string; getOptionLabel?: (option: T) => string; filterBy?: ((option: T) => string) | string; noOptionsText?: string; dropdownMaxHeight?: string; renderAddButton?: any; className?: string; renderOptionIcon?: (option: T) => React.ReactNode; placeholder?: string; showLoading?: boolean; }; function SearchSelector({ options, onSelect, label, dropdownLabel, getOptionLabel, filterBy, noOptionsText, dropdownMaxHeight, renderAddButton, className, renderOptionIcon, placeholder = "Find or add a tag...", // legacy value to not break existing code showLoading = false, }: Props) { const [isExpanded, setIsExpanded] = useState(false); const [filter, setFilter] = useState(""); const handleOptionClick = (e: any, option: any) => { setIsExpanded(false); onSelect(option); setFilter(""); }; const getLabel = (option: any) => { if (typeof getOptionLabel === "function") { return getOptionLabel(option); } return React.isValidElement(option) ? option : ""; }; const filteredOptions = useMemo(() => { if (typeof filterBy === "function") { return options.filter((option) => filterBy(option).includes(filter)); } if (typeof filterBy === "string") { return options.filter((option) => _.get(option, filterBy).includes(filter) ); } return options.filter((option) => typeof option === "string" ? option.includes(filter) : true ); }, [filter, options]); if (showLoading) { return ( <> {label?.length ? : null} ); } return ( <> {label?.length ? : null} { setIsExpanded(false); }} className={className} > { setIsExpanded(false); e.stopPropagation(); setIsExpanded(true); }} onChange={(e) => setFilter(e.target.value)} /> {isExpanded ? ( {!filteredOptions.length ? ( <> {!renderAddButton ? ( {noOptionsText || "No options available for this filter"} ) : (
{ e.stopPropagation(); e.preventDefault(); setFilter(""); }} > {renderAddButton()}
)} ) : ( <> {renderAddButton && (
{ e.stopPropagation(); e.preventDefault(); setFilter(""); }} > {renderAddButton()}
)} {!renderAddButton && dropdownLabel && ( {dropdownLabel} )} {filteredOptions.map((option, i) => ( ))} )}
) : null}
); } export default SearchSelector; const InputWrapper = styled.div` display: flex; margin-bottom: -1px; align-items: center; border: 1px solid #ffffff55; border-radius: 3px; background: #ffffff11; position: relative; width: 100%; min-height: 37px; `; const Input = styled.input` outline: none; border: none; font-size: 13px; background: none; color: #ffffff; padding: 5px 10px; min-height: 35px; max-height: 45px; width: 100%; `; const Label = styled.div` color: #ffffff; margin-bottom: 10px; display: flex; align-items: center; font-size: 13px; font-family: "Work Sans", sans-serif; `; const DropdownWrapper = styled.div` position: absolute; width: 100%; right: 0; z-index: 9999; top: calc(100% + 5px); `; const Dropdown = styled.div` background: #26282f; max-height: ${(props: { dropdownMaxHeight: string }) => props.dropdownMaxHeight || "300px"}; border-radius: 3px; z-index: 999; overflow-y: auto; margin-bottom: 20px; box-shadow: 0 8px 20px 0px #00000088; `; const DropdownLabel = styled.div` font-size: 13px; color: #ffffff44; font-weight: 500; margin: 10px 13px; `; const Option = styled.div` width: 100%; border-top: 1px solid #00000000; border-bottom: 1px solid #ffffff15; min-height: 35px; font-size: 13px; align-items: center; display: flex; align-items: center; padding-left: 15px; cursor: pointer; padding-right: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; :last-child { border-bottom: 1px solid #ffffff00; } :hover { background: #ffffff22; } `;