UNPKG

@nish1896/rhf-mui-components

Version:

A suite of 20+ reusable Material UI components for React Hook Form to minimize your time and effort in creating and styling forms

124 lines (123 loc) 6.93 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useContext } from 'react'; import { Controller } from 'react-hook-form'; import { useTheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; import MuiTextField from '@mui/material/TextField'; import { RHFMuiConfigContext } from '../../config/ConfigProvider'; import { FormControl, FormLabel, FormLabelText, FormHelperText } from '../../mui/common'; import { fieldNameToLabel, keepLabelAboveFormField, isAboveMuiV5 } from '../../utils'; const RHFTagsInput = ({ fieldName, control, registerOptions, onValueChange, label, showLabelAboveFormField, formLabelProps, required, helperText, errorMessage, hideErrorMessage, formHelperTextProps, ChipProps, disabled, sx: muiTextFieldSx, variant = 'outlined', limitTags = 2, getLimitTagsText, slotProps, ...rest }) => { const muiTheme = useTheme(); const [inputValue, setInputValue] = useState(''); const [isFocused, setIsFocused] = useState(false); const { allLabelsAboveFields } = useContext(RHFMuiConfigContext); const isError = Boolean(errorMessage); const fieldLabel = label ?? fieldNameToLabel(fieldName); const isLabelAboveFormField = keepLabelAboveFormField(showLabelAboveFormField, allLabelsAboveFields); /** * Similar to MuiAutocomplete, if limitTags = -1, show all the * tags in the input, even when it is not focused. */ const showAllTags = limitTags === -1; const getTextFieldPadding = (variant) => { switch (variant) { case 'filled': return muiTheme.components?.MuiFilledInput?.styleOverrides?.root?.padding ?? '25px 12px 8px'; case 'standard': return muiTheme.components?.MuiInput?.styleOverrides?.root?.padding ?? '4px 0px 5px'; default: return muiTheme.components?.MuiOutlinedInput?.styleOverrides?.root?.padding ?? '16.5px 14px'; } }; const handleFocus = () => setIsFocused(true); const handleBlur = () => setIsFocused(false); const handleInputChange = (event) => { setInputValue(event.target.value); }; const handleKeyPress = (event, currentTags) => { /* If inputValue is not empty, handle adding a new tag on 'Enter' */ if (event.key === 'Enter') { event.preventDefault(); const newTag = inputValue.trim(); if (newTag) { setInputValue(''); return [...currentTags, newTag]; } } /** * If inputValue is empty, handle removing the last tag on 'Backspace' * or 'Delete'. */ if (inputValue.trim() === '' && (event.key === 'Backspace' || event.key === 'Delete')) { const lastTag = currentTags[currentTags.length - 1]; if (lastTag) { const updatedTags = currentTags.slice(0, currentTags.length - 1); return updatedTags; } } return null; }; const handlePaste = (event, values) => { event.preventDefault(); const pasteData = event.clipboardData.getData('text'); const newTags = pasteData .split(/[\s,]+/) .filter(tag => tag.trim() && !values.includes(tag.trim())); return [...values, ...newTags]; }; return (_jsxs(FormControl, { error: isError, children: [_jsx(FormLabel, { label: fieldLabel, isVisible: isLabelAboveFormField, required: required, error: isError, formLabelProps: formLabelProps }), _jsx(Controller, { name: fieldName, control: control, rules: registerOptions, render: ({ field }) => { const { value = [], onChange } = field; const hideInput = disabled && value.length > 0; const visibleTags = showAllTags ? value : isFocused || !limitTags ? value : value.slice(0, limitTags); const triggerChangeEvents = (fieldValue) => { onChange(fieldValue); onValueChange?.(fieldValue); }; const startAdornment = (_jsxs(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 1, mb: value.length > 0 && !hideInput ? 1 : 0, width: '100%' }, children: [visibleTags.map((tag, index) => (_jsx(Chip, { label: tag, disabled: disabled, onDelete: () => { const newValues = value.filter(item => item !== tag); triggerChangeEvents(newValues); }, ...ChipProps }, index))), !showAllTags && !isFocused && value.length > limitTags && (_jsx(Chip, { label: getLimitTagsText?.(value.length - limitTags) ?? `+${value.length - limitTags} more`, disabled: true }))] })); return (_jsx(MuiTextField, { autoComplete: fieldName, variant: variant, label: !isLabelAboveFormField ? (_jsx(FormLabelText, { label: fieldLabel, required: required })) : undefined, value: inputValue, onFocus: handleFocus, onBlur: handleBlur, onChange: handleInputChange, onKeyDown: event => { const newTags = handleKeyPress(event, value); if (newTags) { triggerChangeEvents(newTags); } }, onPaste: event => { triggerChangeEvents(handlePaste(event, value)); }, disabled: disabled, sx: { ...muiTextFieldSx, '& .MuiInputBase-root': { display: 'flex', flexDirection: 'column', padding: getTextFieldPadding(variant) }, '& .MuiInputBase-input': { padding: 0, ...(hideInput && { display: 'none' }) } }, ...(isAboveMuiV5 ? { slotProps: { ...slotProps, input: { startAdornment } } } : { InputProps: { startAdornment } }), ...rest })); } }), _jsx(FormHelperText, { error: isError, errorMessage: errorMessage, hideErrorMessage: hideErrorMessage, helperText: helperText, formHelperTextProps: formHelperTextProps })] })); }; export default RHFTagsInput;