import styled from '@emotion/styled'; import type { CSSProperties, ForwardedRef, SelectHTMLAttributes } from 'react'; import { useCallback } from 'react'; import { forwardRefWithGeneric } from '../utility/forwardRef.js'; const arrowDownIcon = `url('data:image/svg+xml;utf8,')`; const BaseSelect = styled.select<{ width: number | string | undefined }>` align-self: center; appearance: none; background: ${arrowDownIcon} no-repeat right white; background-position-x: calc(100% - 5px); background-size: 15px 15px; border: 0.55px solid #cacaca; border-radius: 5px; font-size: 14px; height: 100%; margin: 0; padding: 0 5px; width: ${({ width }) => (width ? Number(width) - 5 : 115)}px; :focus, input:focus { outline: none; } :required:invalid { color: #666; } `; interface SelectProps extends Omit< SelectHTMLAttributes, 'style' | 'onChange' > { onChange?: (element: string) => void; items: T[]; defaultValue?: string | number | undefined; style?: CSSProperties; placeholder?: string; itemValueField?: keyof T; itemTextField?: keyof T; returnValue?: boolean; textRender?: (text: string) => string; } const Select = forwardRefWithGeneric(function Select( props: SelectProps, ref: ForwardedRef, ) { const { items, style = { width: 100 }, onChange = () => null, defaultValue, value, name = '', className = '', placeholder = '', itemValueField = 'value' as keyof T, itemTextField = 'label' as keyof T, returnValue = true, textRender, ...otherProps } = props; const handleOnChanged = useCallback( (e: any) => { const [option] = e.target.selectedOptions; const selectedData = JSON.parse(option.dataset.value); onChange(returnValue ? selectedData[itemValueField] : selectedData); }, [itemValueField, onChange, returnValue], ); return ( {placeholder && ( )} {items.map((option) => { const value = option[itemValueField]; if (!['number', 'string'].includes(typeof option[itemTextField])) { // eslint-disable-next-line no-console console.log( `The '${String(itemTextField)}' field should be a string or number, option : ${JSON.stringify( option, )}`, ); return null; } if (!['number', 'string'].includes(typeof value)) { // eslint-disable-next-line no-console console.log( `The '${String(itemValueField)}' field should be either a string or a number, option : ${JSON.stringify( option, )}`, ); return null; } const label = String(option[itemTextField]); return ( ); })} ); }); export default Select;