import React, { useState, KeyboardEvent, FocusEvent, InputHTMLAttributes, ReactNode, ChangeEvent, MouseEvent, } from 'react'; import { HasAlign, HasRef, HasRootRef } from '../../types'; import FormField from '../FormField/FormField'; import { classNames } from '../../lib/classNames'; import Chip, { ChipProps } from '../Chip/Chip'; import { noop } from '../../lib/utils'; import { useChipsInput } from './useChipsInput'; import { withAdaptivity, AdaptivityProps } from '../../hoc/withAdaptivity'; export type ChipsInputValue = string | number; export interface ChipsInputOption { value?: ChipsInputValue; label?: string; [otherProp: string]: any; } export interface RenderChip extends ChipProps { label: string; option: Option; disabled: boolean; } export interface ChipsInputProps extends Omit, 'value' | 'onChange'>, HasRef, HasRootRef, HasAlign, AdaptivityProps { value: Option[]; inputValue?: string; onChange?: (o: Option[]) => void; onInputChange?: (e?: ChangeEvent) => void; getOptionValue?: (o?: Option) => ChipsInputValue; getOptionLabel?: (o?: Option) => string; getNewOptionData?: (v?: ChipsInputValue, l?: string) => Option; renderChip?: (props?: RenderChip) => ReactNode; } const ChipsInput = (props: ChipsInputProps) => { const { style, value, onChange, onInputChange, onKeyDown, onBlur, onFocus, children, className, inputValue, getRef, getRootRef, disabled, placeholder, tabIndex, getOptionValue, getOptionLabel, getNewOptionData, renderChip, sizeY, ...restProps } = props; const [focused, setFocused] = useState(false); const { fieldValue, addOptionFromInput, removeOption, selectedOptions, handleInputChange } = useChipsInput(props); const handleKeyDown = (e: KeyboardEvent) => { onKeyDown(e); if (e.key === 'Backspace' && !e.defaultPrevented && !fieldValue && selectedOptions.length) { removeOption(getOptionValue(selectedOptions[selectedOptions.length - 1])); e.preventDefault(); } if (e.key === 'Enter' && !e.defaultPrevented && fieldValue) { addOptionFromInput(); e.preventDefault(); } }; const handleBlur = (e: FocusEvent) => { if (focused) { setFocused(false); } onBlur(e); }; const handleFocus = (e: FocusEvent) => { if (!focused) { setFocused(true); } onFocus(e); }; const handleChipRemove = (_: MouseEvent, value: ChipsInputValue) => { removeOption(value); }; return ( {selectedOptions.map((option: Option) => { const value = getOptionValue(option); const label = getOptionLabel(option); return ( {renderChip({ option, value, label, onRemove: handleChipRemove, disabled, className: 'ChipsInput__chip' })} ); })} ); }; export const chipsInputDefaultProps: ChipsInputProps = { type: 'text', onChange: noop, onInputChange: noop, onKeyDown: noop, onBlur: noop, onFocus: noop, value: [], tabIndex: 0, inputValue: '', getOptionValue: (option: ChipsInputOption): ChipsInputValue => option.value, getOptionLabel: (option: ChipsInputOption): string => option.label, getNewOptionData: (_: ChipsInputValue, label: string): ChipsInputOption => ({ value: label, label }), renderChip({ disabled, value, label, ...rest }: RenderChip) { return {label}; }, }; ChipsInput.defaultProps = chipsInputDefaultProps; export default withAdaptivity(ChipsInput, { sizeY: true });