UNPKG

@nish1896/rhf-mui-components

Version:

A suite of 20+ production-ready react-hook-form components built with material-ui. Fully typed, tree-shakable, and optimized for enterprise-grade forms.

231 lines (230 loc) 9.27 kB
"use client"; import { RHFMuiConfigContext } from "../../config/ConfigProvider.js"; import { validateArray } from "../../utils/array.js"; import { keepLabelAboveFormField } from "../../utils/control.js"; import { isAboveMuiV5 } from "../../utils/mui.js"; import { isKeyValueOption } from "../../utils/object.js"; import { fieldNameToLabel } from "../../utils/text-transform.js"; import { useFieldIds } from "../../utils/useFieldIds.js"; import { selectAllOptionValue } from "../../common/constants.js"; import FormControl from "../../common/FormControl.js"; import FormHelperText from "../../common/FormHelperText.js"; import FormLabel from "../../common/FormLabel.js"; import FormLabelText from "../../common/FormLabelText.js"; import { useCallback, useContext, useMemo } from "react"; import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime"; import { Controller } from "react-hook-form"; import Autocomplete from "@mui/material/Autocomplete"; import MuiTextField from "@mui/material/TextField"; import Chip from "@mui/material/Chip"; import CircularProgress from "@mui/material/CircularProgress"; import FormControlLabel from "@mui/material/FormControlLabel"; import MuiCheckbox from "@mui/material/Checkbox"; import Box from "@mui/material/Box"; //#region src/mui/multi-autocomplete-object/index.tsx const RHFMultiAutocompleteObject = ({ fieldName, control, registerOptions, options, labelKey, valueKey, selectAllText = "Select All", onValueChange, disabled: muiDisabled, label, showLabelAboveFormField, formLabelProps, checkboxProps, formControlLabelProps, required, helperText, errorMessage, hideErrorMessage, formHelperTextProps, textFieldProps, slotProps, ChipProps, onBlur, loading, hideSelectAllOption, ...otherAutoCompleteProps }) => { validateArray("RHFMultiAutocompleteObject", options, labelKey, valueKey); const { fieldId, labelId, helperTextId, errorId } = useFieldIds(fieldName); const { allLabelsAboveFields, defaultFormControlLabelSx } = useContext(RHFMuiConfigContext); const isLabelAboveFormField = keepLabelAboveFormField(showLabelAboveFormField, allLabelsAboveFields); const fieldLabel = label ?? fieldNameToLabel(fieldName); const { sx, ...otherFormControlLabelProps } = formControlLabelProps ?? {}; const appliedFormControlLabelSx = { ...defaultFormControlLabelSx, ...sx }; const isError = !!errorMessage; const showHelperTextElement = !!helperText || isError && !hideErrorMessage; const shouldHideSelectAllOptions = hideSelectAllOption || options.length === 0 || options.length === 1; const selectAllOption = useMemo(() => ({ [labelKey]: selectAllText, [valueKey]: selectAllOptionValue }), [ labelKey, valueKey, selectAllText ]); const autoCompleteOptions = useMemo(() => { if (shouldHideSelectAllOptions) return options; return [selectAllOption, ...options]; }, [ options, selectAllOption, shouldHideSelectAllOptions ]); const isSelectAllOption = useCallback((option) => { return option[valueKey] === selectAllOptionValue; }, [valueKey]); const getOptionLabelOrValue = useCallback((option, key) => { return String(option[key]); }, []); const renderOptionLabel = useCallback((option, getSelectAllValue) => isSelectAllOption(option) ? getSelectAllValue ? selectAllOptionValue : selectAllText : getOptionLabelOrValue(option, labelKey), [ isSelectAllOption, selectAllText, getOptionLabelOrValue, labelKey ]); return /* @__PURE__ */ jsxs(FormControl, { error: isError, children: [ /* @__PURE__ */ jsx(FormLabel, { label: fieldLabel, isVisible: isLabelAboveFormField, required, error: isError, formLabelProps: { id: labelId, htmlFor: fieldId, ...formLabelProps } }), /* @__PURE__ */ jsx(Controller, { name: fieldName, control, rules: registerOptions, render: ({ field: { name: rhfFieldName, value: rhfValue, onChange: rhfOnChange, onBlur: rhfOnBlur, ref: rhfRef } }) => { const selectedValues = rhfValue ?? []; const selectedOptions = selectedValues.filter((option) => !isSelectAllOption(option)); const areAllSelected = options.length > 0 && selectedOptions.length === options.length; const isIndeterminate = selectedValues.length > 0 && !areAllSelected; return /* @__PURE__ */ jsx(Autocomplete, { id: fieldId, options: autoCompleteOptions, value: selectedOptions, loading, fullWidth: true, multiple: true, autoHighlight: true, disableCloseOnSelect: true, disabled: muiDisabled, onChange: (_, newSelectedOptions, reason, details) => { if (reason === "clear") { rhfOnChange([]); onValueChange?.([]); return; } if (newSelectedOptions.some(isSelectAllOption)) { const finalValue = areAllSelected ? [] : options; rhfOnChange(finalValue); onValueChange?.(finalValue, selectAllOption); return; } const clickedOption = details?.option; const finalValue = newSelectedOptions.filter((option) => !isSelectAllOption(option)); rhfOnChange(finalValue); onValueChange?.(finalValue, clickedOption); }, onBlur: (blurEvent) => { rhfOnBlur(); onBlur?.(blurEvent); }, limitTags: 2, getLimitTagsText: (value) => `+${value} More`, getOptionLabel: (option) => renderOptionLabel(option, true), isOptionEqualToValue: (option, value) => { if (isSelectAllOption(option)) return false; if (valueKey && isKeyValueOption(option, labelKey, valueKey)) return option[valueKey] === value[valueKey]; return option === value; }, renderInput: (params) => { const { InputProps, inputProps, disabled: paramsDisabled, ...otherInputParams } = params ?? {}; const { autoComplete = "off", placeholder, ...otherTextFieldProps } = textFieldProps ?? {}; const textFieldInputProps = { ...inputProps, "aria-required": required, "aria-invalid": isError, "aria-labelledby": isLabelAboveFormField ? labelId : void 0, "aria-describedby": showHelperTextElement ? isError ? errorId : helperTextId : void 0, autoComplete }; return /* @__PURE__ */ jsx(MuiTextField, { name: rhfFieldName, inputRef: rhfRef, disabled: paramsDisabled || muiDisabled, ...otherTextFieldProps, placeholder: selectedOptions.length > 0 ? void 0 : placeholder, ...otherInputParams, label: !isLabelAboveFormField ? /* @__PURE__ */ jsx(FormLabelText, { label: fieldLabel, required }) : void 0, error: isError, ...isAboveMuiV5 ? { slotProps: { ...textFieldProps?.slotProps, input: { ...InputProps, ...textFieldProps?.slotProps?.input, endAdornment: /* @__PURE__ */ jsxs(Fragment$1, { children: [loading && /* @__PURE__ */ jsx(CircularProgress, { color: "inherit", size: 20 }), InputProps.endAdornment] }) }, htmlInput: textFieldInputProps } } : { InputProps: { ...InputProps, ...textFieldProps?.InputProps, endAdornment: /* @__PURE__ */ jsxs(Fragment$1, { children: [loading && /* @__PURE__ */ jsx(CircularProgress, { color: "inherit", size: 20 }), InputProps.endAdornment] }) }, inputProps: textFieldInputProps } }); }, renderOption: (optionProps, option) => { const isSelectAll = isSelectAllOption(option); const opnLabel = renderOptionLabel(option); const opnValue = isSelectAll ? selectAllOptionValue : getOptionLabelOrValue(option, valueKey); return /* @__PURE__ */ jsx(Box, { component: "li", ...optionProps, children: /* @__PURE__ */ jsx(FormControlLabel, { label: opnLabel, onClick: (e) => e.preventDefault(), control: /* @__PURE__ */ jsx(MuiCheckbox, { ...checkboxProps, id: `${fieldName}_${opnValue}`, name: `${fieldName}_${opnValue}`, value: opnValue, checked: isSelectAll ? areAllSelected : selectedOptions.some((selected) => getOptionLabelOrValue(selected, valueKey) === opnValue), indeterminate: isSelectAll ? isIndeterminate : void 0 }), sx: { ...appliedFormControlLabelSx, width: "100%" }, ...otherFormControlLabelProps }) }); }, renderTags: (value, getTagProps) => value.map((option, index) => { const { key, ...otherChipProps } = getTagProps({ index }); return /* @__PURE__ */ jsx(Chip, { ...otherChipProps, label: renderOptionLabel(option), ...ChipProps }, key); }), ...isAboveMuiV5 ? { slotProps } : { ChipProps }, ...otherAutoCompleteProps }); } }), /* @__PURE__ */ jsx(FormHelperText, { error: isError, errorMessage, hideErrorMessage, helperText, showHelperTextElement, formHelperTextProps: { id: isError ? errorId : helperTextId, ...formHelperTextProps } }) ] }); }; //#endregion export { RHFMultiAutocompleteObject as default };