UNPKG

@nish1896/rhf-mui-components

Version:

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

186 lines (185 loc) 7.78 kB
"use client"; import { RHFMuiConfigContext } from "../../config/ConfigProvider.js"; import { keepLabelAboveFormField, mergeRefs } from "../../utils/control.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 { isKeyValueOption } from "../../utils/object.js"; import { fieldNameToLabel } from "../../utils/text-transform.js"; import { useFieldIds } from "../../utils/useFieldIds.js"; import { forwardRef, 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 TextField from "@mui/material/TextField"; import CircularProgress from "@mui/material/CircularProgress"; //#region src/mui/autocomplete/index.tsx const RHFAutocomplete = forwardRef(function RHFAutocomplete({ fieldName, control, registerOptions, options, labelKey, valueKey, multiple, disableClearable, freeSolo, autoHighlight = true, onValueChange, customOnChange, disabled: muiDisabled, label, showLabelAboveFormField, formLabelProps, hideLabel, required, helperText, errorMessage, hideErrorMessage, formHelperTextProps, textFieldProps, slotProps, ChipProps, onBlur, onFocus, loading, limitTags = 2, customIds, autoSelect, getLimitTagsText, ...otherAutoCompleteProps }, ref) { const { allLabelsAboveFields } = useContext(RHFMuiConfigContext); const { fieldId, labelId, helperTextId, errorId } = useFieldIds(fieldName, customIds); const isLabelAboveFormField = keepLabelAboveFormField(showLabelAboveFormField, allLabelsAboveFields); const defaultFieldLabel = fieldNameToLabel(fieldName); const fieldLabel = label ?? defaultFieldLabel; const accessibleFieldLabel = typeof fieldLabel === "string" ? fieldLabel : defaultFieldLabel; const optionsMap = useMemo(() => { if (!valueKey) return null; const map = /* @__PURE__ */ new Map(); for (const option of options) if (isKeyValueOption(option, labelKey, valueKey)) map.set(option[valueKey], option); return map; }, [ options, valueKey, labelKey ]); const renderOptionLabel = useCallback((option) => { if (typeof option === "string") return option; if (labelKey && isKeyValueOption(option, labelKey, valueKey)) return String(option[labelKey]); return String(option); }, [labelKey, valueKey]); return /* @__PURE__ */ jsx(Controller, { name: fieldName, control, rules: registerOptions, render: ({ field: { name: rhfFieldName, value: rhfValue, onChange: rhfOnChange, onBlur: rhfOnBlur, ref: rhfRef, disabled: rhfDisabled }, fieldState: { error: fieldStateError } }) => { const isDisabled = muiDisabled || rhfDisabled; const fieldErrorMessage = fieldStateError?.message?.toString() ?? errorMessage; const isError = !!fieldErrorMessage; const showHelperTextElement = !!(helperText || isError && !hideErrorMessage); let selectedOptions; if (multiple) { const multiValue = []; for (const val of Array.isArray(rhfValue) ? rhfValue : []) { const option = optionsMap ? optionsMap.get(val) : options.find((opn) => opn === val); if (option) multiValue.push(option); else if (freeSolo) multiValue.push(String(val)); } selectedOptions = multiValue; } else selectedOptions = rhfValue === null || rhfValue === void 0 ? null : optionsMap ? optionsMap.get(rhfValue) ?? (freeSolo ? rhfValue : null) : options.find((opn) => opn === rhfValue) ?? (freeSolo ? rhfValue : null); return /* @__PURE__ */ jsxs(FormControl, { error: isError, disabled: isDisabled, children: [ !hideLabel && /* @__PURE__ */ jsx(FormLabel, { label: fieldLabel, isVisible: isLabelAboveFormField, required, error: isError, disabled: isDisabled, formLabelProps: { ...formLabelProps, id: labelId, htmlFor: fieldId } }), /* @__PURE__ */ jsx(Autocomplete, { ...otherAutoCompleteProps, id: fieldId, options, multiple, freeSolo, autoSelect: freeSolo ? autoSelect ?? true : autoSelect, value: selectedOptions, disabled: isDisabled, onChange: (event, newValue, reason, details) => { const storedValue = newValue === null ? null : Array.isArray(newValue) ? newValue.map((item) => typeof item !== "string" && valueKey && isKeyValueOption(item, labelKey, valueKey) ? item[valueKey] : item) : typeof newValue !== "string" && valueKey && isKeyValueOption(newValue, labelKey, valueKey) ? newValue[valueKey] : newValue; if (customOnChange) { customOnChange({ rhfOnChange, newValue: storedValue, selectedOption: newValue, event, reason, details }); return; } rhfOnChange(storedValue); onValueChange?.({ newValue: storedValue, selectedOption: newValue, event, reason, details }); }, onFocus, onBlur: (blurEvent) => { rhfOnBlur(); onBlur?.(blurEvent); }, getOptionLabel: renderOptionLabel, isOptionEqualToValue: (option, value) => { if (!value) return false; if (typeof option === "string") return option === (typeof value === "string" ? value : String(value)); if (valueKey && isKeyValueOption(option, labelKey, valueKey)) return option[valueKey] === (typeof value === "object" && value !== null ? value[valueKey] : value); return option === value; }, renderInput: (params) => { const { InputProps, inputProps, disabled: paramsDisabled, ...otherInputParams } = params ?? {}; const { autoComplete = "off", ...otherTextFieldProps } = textFieldProps ?? {}; const textFieldInputProps = { ...inputProps, "aria-required": required, "aria-invalid": isError, "aria-labelledby": !hideLabel && isLabelAboveFormField ? labelId : void 0, "aria-label": hideLabel ? accessibleFieldLabel : void 0, "aria-describedby": showHelperTextElement ? isError ? errorId : helperTextId : void 0, autoComplete }; return /* @__PURE__ */ jsx(TextField, { name: rhfFieldName, inputRef: mergeRefs(rhfRef, ref), disabled: paramsDisabled, ...otherTextFieldProps, ...otherInputParams, label: !isLabelAboveFormField ? /* @__PURE__ */ jsx(FormLabelText, { label: fieldLabel, required }) : void 0, error: isError, 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 } }); }, autoHighlight, blurOnSelect: !multiple, disableCloseOnSelect: multiple, disableClearable, fullWidth: true, loading, limitTags, getLimitTagsText: (more) => getLimitTagsText?.(more) ?? `+${more} More`, slotProps: { ...slotProps, chip: ChipProps } }), /* @__PURE__ */ jsx(FormHelperText, { error: isError, errorMessage: fieldErrorMessage, hideErrorMessage, helperText, showHelperTextElement, formHelperTextProps: { ...formHelperTextProps, id: isError ? errorId : helperTextId } }) ] }); } }); }); //#endregion export { RHFAutocomplete as default };