Files
serpbear/components/common/SelectField.tsx
towfiqi 4d15989b28 feat: Adds a Keyword Research Section.
- Adds a /research page to the app that lets users generate keyword ideas based on given keywords.
- Allows the ability to export keywords.
2024-02-29 11:37:45 +06:00

134 lines
5.4 KiB
TypeScript

import React, { useMemo, useState } from 'react';
import Icon from './Icon';
export type SelectionOption = {
label:string,
value: string
}
type SelectFieldProps = {
defaultLabel: string,
options: SelectionOption[],
selected: string[],
label?: string,
multiple?: boolean,
updateField: Function,
fullWidth?: boolean,
minWidth?: number,
maxHeight?: number|string,
rounded?: string,
flags?: boolean,
inline?: boolean,
emptyMsg?: string
}
const SelectField = (props: SelectFieldProps) => {
const {
options,
selected,
defaultLabel = 'Select an Option',
multiple = true,
updateField,
minWidth = 180,
maxHeight = 96,
fullWidth = false,
rounded = 'rounded-3xl',
inline = false,
flags = false,
label = '',
emptyMsg = '' } = props;
const [showOptions, setShowOptions] = useState<boolean>(false);
const [filterInput, setFilterInput] = useState<string>('');
const [filterdOptions, setFilterdOptions] = useState<SelectionOption[]>([]);
const selectedLabels = useMemo(() => {
return options.reduce((acc:string[], item:SelectionOption) :string[] => {
return selected.includes(item.value) ? [...acc, item.label] : [...acc];
}, []);
}, [selected, options]);
const selectItem = (option:SelectionOption) => {
let updatedSelect = [option.value];
if (multiple && Array.isArray(selected)) {
if (selected.includes(option.value)) {
updatedSelect = selected.filter((x) => x !== option.value);
} else {
updatedSelect = [...selected, option.value];
}
}
updateField(updatedSelect);
if (!multiple) { setShowOptions(false); }
};
const filterOptions = (event:React.FormEvent<HTMLInputElement>) => {
setFilterInput(event.currentTarget.value);
const filteredItems:SelectionOption[] = [];
const userVal = event.currentTarget.value.toLowerCase();
options.forEach((option:SelectionOption) => {
if (flags ? option.label.toLowerCase().startsWith(userVal) : option.label.toLowerCase().includes(userVal)) {
filteredItems.push(option);
}
});
setFilterdOptions(filteredItems);
};
return (
<div className={`select font-semibold text-gray-500 relative ${inline ? 'inline-block' : 'flex'} justify-between items-center"`}>
{label && <label className='mb-2 font-semibold inline-block text-sm text-gray-700 capitalize'>{label}</label>}
<div
className={`selected flex border ${rounded} p-1.5 px-4 cursor-pointer select-none ${fullWidth ? 'w-full' : 'w-[210px]'}
min-w-[${minWidth}px] ${showOptions ? 'border-indigo-200' : ''}`}
onClick={() => setShowOptions(!showOptions)}>
<span className={'w-full inline-block truncate mr-2 capitalize'}>
{selected.length > 0 ? (selectedLabels.slice(0, 2).join(', ')) : defaultLabel}
</span>
{multiple && selected.length > 2
&& <span className={`px-2 py-0 ${rounded} bg-[#eaecff] text-[0.7rem] font-bold`}>{(selected.length)}</span>}
<span className='ml-2'><Icon type={showOptions ? 'caret-up' : 'caret-down'} size={9} /></span>
</div>
{showOptions && (
<div
className={`select_list mt-1 border absolute min-w-[${minWidth}px] top-[30px] right-0 ${fullWidth ? 'w-full' : 'w-[210px]'}
${rounded === 'rounded-3xl' ? 'rounded-lg' : rounded} max-h-${maxHeight} bg-white z-50 overflow-y-auto styled-scrollbar`}>
{options.length > 20 && (
<div className=''>
<input
className=' border-b-[1px] p-3 w-full focus:outline-0 focus:border-blue-100'
type="text"
placeholder='Search..'
onChange={filterOptions}
value={filterInput}
/>
</div>
)}
<ul>
{(options.length > 20 && filterdOptions.length > 0 && filterInput ? filterdOptions : options).map((opt) => {
const itemActive = selected.includes(opt.value);
return (
<li
key={opt.value}
className={`select-none cursor-pointer px-3 py-2 hover:bg-[#FCFCFF] capitalize text-ellipsis overflow-hidden
${itemActive ? ' bg-indigo-50 text-indigo-600 hover:bg-indigo-50' : ''} `}
onClick={() => selectItem(opt)}
>
{multiple && (
<span
className={`p-0 mr-2 leading-[0px] inline-block rounded-sm pt-0 px-[1px] pb-[3px] border
${itemActive ? ' bg-indigo-600 border-indigo-600 text-white' : 'text-transparent'}`} >
<Icon type="check" size={10} />
</span>
)}
{flags && <span className={`fflag fflag-${opt.value} w-[15px] h-[10px] mr-1`} />}
{opt.label}
</li>
);
})}
</ul>
{emptyMsg && options.length === 0 && <p className='p-4'>{emptyMsg}</p>}
</div>
)}
</div>
);
};
export default SelectField;