Skip to content

Instantly share code, notes, and snippets.

@docentedev
Created December 15, 2022 19:29
Show Gist options
  • Save docentedev/236114ef8140c488bf3250dae0122b1b to your computer and use it in GitHub Desktop.
Save docentedev/236114ef8140c488bf3250dae0122b1b to your computer and use it in GitHub Desktop.
dd.tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useRef, useState } from 'react';
import useClickOutside from '../../hooks/useClickOutside';
import Hint from '../hint';
import Icon from '../icon/v1';
import { Container } from './index.style';
import { textNormalize } from './index.utils';
type Option = {
value: string;
label: string;
persistent?: boolean;
};
type DropdownProps = {
label: string;
value: string;
onChange: (key: string) => void;
options: Option[];
direction?: 'top' | 'bottom';
variant?: 'default' | 'success' | 'error' | 'warning';
hint?: string;
};
const ICON_SIZE = 24;
const getIcon = (variant: 'default' | 'success' | 'error' | 'warning') => {
if (variant === 'default') return <span />;
if (variant === 'success') return <Icon name="SuccessCircle" size={ICON_SIZE} />;
if (variant === 'error') return <Icon name="AlertCircle" size={ICON_SIZE} />;
if (variant === 'warning') return <Icon name="AlertTriangle" size={ICON_SIZE} />;
return '';
};
/**
*
* @example ```js
* const [opt, setOpt] = useState('');
const options = [
{ label: 'All', value: 'all', persistent: true },
{ label: 'Afghanistan', value: 'AF' },
];
return (
<>
<button onClick={() => setOpt('')}>Clear</button>
<Dropdown label="Pais" options={options} onChange={setOpt} value={opt} />
<Dropdown label="Pais" options={options} onChange={setOpt} value={opt} variant="error" direction="top" />
<Dropdown label="Pais" options={options} onChange={setOpt} value={opt} variant="success" hint="Hint" />
<Dropdown label="Pais" options={options} onChange={setOpt} value={opt} variant="warning" hint="Hint" />
<Dropdown hint="Hint" label="Pais" options={options} onChange={setOpt} value={opt} direction="top" />
</>
);
* ```
*/
const Dropdown = ({
label,
options,
value,
onChange,
variant = 'default',
direction = 'bottom',
hint,
}: DropdownProps) => {
const ref = useRef<HTMLElement>();
const inputRef = useRef<HTMLInputElement>();
const [open, setOpen] = useState(false);
const [focus, setFocus] = useState(false);
const [filter, setFilter] = useState('');
useClickOutside(ref, () => {
setOpen(false);
setFilter('');
});
const handleFilter = (event: any) => {
setFilter(event.target.value);
};
const handleFocus = () => {
setFocus(true);
setOpen(true);
};
const handleBlur = () => {
setFocus(false);
};
const handleClose = (event) => {
event.stopPropagation();
setOpen(false);
};
const handleOpen = () => {
setOpen(true);
if (ref.current) {
ref.current.focus();
}
};
const handleFocusInput = () => {
if (ref.current) {
ref.current.focus();
}
};
const wrapperHandleSetOption = (option: Option) => () => {
setOpen(false);
setFilter('');
onChange(option.value);
};
const getFinded = (option: Option) => textNormalize(option.label).indexOf(textNormalize(filter)) !== -1;
const getSelectedLabel = () => options.find((o) => o.value === value)?.label || '';
return (
<Container
ref={ref}
data-direction={direction}
data-hint={!!hint}
data-variant={variant}
data-open={open}
data-label-selected={!!getSelectedLabel()}
data-focus={focus}
>
<div onClick={handleOpen}>
<div>
<label onClick={handleFocusInput}>{label}</label>
<input
ref={inputRef as any}
onBlur={handleBlur}
onFocus={handleFocus}
value={filter}
onChange={handleFilter}
placeholder={open ? getSelectedLabel() : ''}
/>
{getIcon(variant)}
<span data-open-searchable={open}>
{open ? (
<div onClick={handleClose}>
<Icon name="ChevronUp" />
</div>
) : (
<Icon name="ChevronDown" />
)}
</span>
</div>
<div>{getSelectedLabel()}</div>
</div>
{hint && <Hint variant={variant}>{hint}</Hint>}
<div>
{options.map((option: Option, index: number) => (
<button
data-finded={getFinded(option)}
data-persistent={!!option.persistent}
data-active={option.value === value}
data-label={option.label}
key={`${option.value}-${index}`}
onClick={wrapperHandleSetOption(option)}
>
{option.label}
</button>
))}
</div>
</Container>
);
};
export default Dropdown;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment