@chris-c-brine/rhf-mui-kit 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,8 +13,8 @@ A specialized component library that extends React Hook Form with Material UI in
13
13
 
14
14
  ## Components
15
15
 
16
- - **AutocompleteDisplayElement**: Enhanced autocomplete with view-only mode support
17
- - **ObjectDisplayElement**: Autocomplete for complex objects with key/label extraction
16
+ - **AutocompleteElementDisplay**: Enhanced autocomplete with view-only mode support
17
+ - **ObjectElementDisplay**: Autocomplete for complex objects with key/label extraction
18
18
  - **ValidationElement**: Hidden form field with validation support
19
19
 
20
20
  ## Hooks
@@ -38,13 +38,13 @@ npm install @chris-c-brine/rhf-mui-kit
38
38
  ## Basic Usage
39
39
 
40
40
  ```tsx
41
- import { ObjectDisplayElement } from '@chris-c-brine/rhf-mui-kit';
41
+ import { ObjectElementDisplay } from '@chris-c-brine/rhf-mui-kit';
42
42
  import { FormContainer } from 'react-hook-form-mui';
43
43
 
44
44
  // Example with object values
45
45
  const MyForm = () => (
46
46
  <FormContainer defaultValues={{ user: null }}>
47
- <ObjectDisplayElement
47
+ <ObjectElementDisplay
48
48
  name="user"
49
49
  label="Select User"
50
50
  options={users}
@@ -3,7 +3,7 @@ import { type ChipProps, type ChipTypeMap } from "@mui/material";
3
3
  import type { FieldPath, FieldValues } from "react-hook-form";
4
4
  import { type AutocompleteElementDisplayProps } from "./AutocompleteElementDisplay";
5
5
  /**
6
- * Extends DisplayAutocompleteProps with additional properties for handling object values.
6
+ * Extends AutocompleteElementDisplayProps with additional properties for handling object values.
7
7
  *
8
8
  * @template TValue - The type of the option values
9
9
  * @template Multiple - Boolean flag indicating if multiple selections are allowed
@@ -13,7 +13,7 @@ import { type AutocompleteElementDisplayProps } from "./AutocompleteElementDispl
13
13
  * @template TFieldValues - The type of the form values
14
14
  * @template TName - The type of the field name
15
15
  */
16
- export type DisplayObjectCompleteProps<TValue, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends ElementType = ChipTypeMap["defaultComponent"], TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = AutocompleteElementDisplayProps<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent, TFieldValues, TName> & {
16
+ export type ObjectElementDisplayProps<TValue, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends ElementType = ChipTypeMap["defaultComponent"], TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = AutocompleteElementDisplayProps<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent, TFieldValues, TName> & {
17
17
  /**
18
18
  * Function to extract a unique key from an option value.
19
19
  * Used for option comparison and deduplication.
@@ -57,7 +57,7 @@ export type DisplayObjectCompleteProps<TValue, Multiple extends boolean | undefi
57
57
  };
58
58
  /**
59
59
  * A form component that displays a searchable dropdown for selecting object values.
60
- * Extends AutocompleteDisplayElement with object-specific functionality.
60
+ * Extends AutocompleteElementDisplay with object-specific functionality.
61
61
  *
62
62
  * Features:
63
63
  * - Works with complex object values instead of just primitive types
@@ -76,4 +76,4 @@ export type DisplayObjectCompleteProps<TValue, Multiple extends boolean | undefi
76
76
  *
77
77
  * @returns A React component for selecting object values
78
78
  */
79
- export declare const ObjectDisplayElement: <TValue, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends ElementType = ChipTypeMap["defaultComponent"], TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ options, autocompleteProps, getItemKey, getItemLabel, getChipProps, stringToNewItem, name, freeSolo, control, ...props }: DisplayObjectCompleteProps<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent, TFieldValues, TName>) => import("react/jsx-runtime").JSX.Element;
79
+ export declare const ObjectElementDisplay: <TValue, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends ElementType = ChipTypeMap["defaultComponent"], TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ options, autocompleteProps, getItemKey, getItemLabel, getChipProps, stringToNewItem, name, freeSolo, control, ...props }: ObjectElementDisplayProps<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent, TFieldValues, TName>) => import("react/jsx-runtime").JSX.Element;
@@ -10,7 +10,7 @@ import lodash from "lodash";
10
10
  const { omit } = lodash;
11
11
  /**
12
12
  * A form component that displays a searchable dropdown for selecting object values.
13
- * Extends AutocompleteDisplayElement with object-specific functionality.
13
+ * Extends AutocompleteElementDisplay with object-specific functionality.
14
14
  *
15
15
  * Features:
16
16
  * - Works with complex object values instead of just primitive types
@@ -29,7 +29,7 @@ const { omit } = lodash;
29
29
  *
30
30
  * @returns A React component for selecting object values
31
31
  */
32
- export const ObjectDisplayElement = ({ options, autocompleteProps, getItemKey, getItemLabel, getChipProps, stringToNewItem, name, freeSolo, control, ...props }) => {
32
+ export const ObjectElementDisplay = ({ options, autocompleteProps, getItemKey, getItemLabel, getChipProps, stringToNewItem, name, freeSolo, control, ...props }) => {
33
33
  /**
34
34
  * Access to the form field controller
35
35
  */
@@ -172,8 +172,6 @@ export const ObjectDisplayElement = ({ options, autocompleteProps, getItemKey, g
172
172
  return typedValue.map((v, index) => {
173
173
  // @ts-expect-error a key is returned, and the linter doesn't pick this up
174
174
  const { key, ...chipProps } = getItemProps({ index });
175
- // const { key, ...rawChipProps } = getItemProps({ index });
176
- // const chipProps = omit(rawChipProps, "onDelete");
177
175
  const label = typeof v === "string" ? v : getItemLabel(v);
178
176
  // Get additional chip props based on the value if the function is provided
179
177
  const valueSpecificProps = typeof v !== "string" && getChipProps ? getChipProps({ value: v, index }) : {};
@@ -181,7 +179,6 @@ export const ObjectDisplayElement = ({ options, autocompleteProps, getItemKey, g
181
179
  });
182
180
  }
183
181
  // @ts-expect-error a key is returned, and the linter doesn't pick this up
184
- // const { key, ...rawChipProps } = getItemProps({ index: 0 });
185
182
  const { key, ...rawChipProps } = getItemProps({ index: 0 });
186
183
  const itemProps = omit(rawChipProps, "onDelete");
187
184
  return (_jsx(Typography, { component: "span", color: "text.primary", ...(props?.viewOnly ? omit(itemProps, "disabled") : itemProps), children: (typeof typedValue === "string") ? typedValue : getItemLabel(typedValue) }, `${name}-value-${key}`));
@@ -1,3 +1,3 @@
1
1
  export * from "./AutocompleteElementDisplay";
2
- export * from "./ObjectDisplayElement";
2
+ export * from "./ObjectElementDisplay";
3
3
  export * from "./ValidationElement";
@@ -1,3 +1,3 @@
1
1
  export * from "./AutocompleteElementDisplay";
2
- export * from "./ObjectDisplayElement";
2
+ export * from "./ObjectElementDisplay";
3
3
  export * from "./ValidationElement";
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("react/jsx-runtime"),$=require("react-hook-form-mui"),l=require("react"),M=require("lodash"),x=require("@mui/material"),G=require("react-hook-form"),{merge:q}=M,D=({viewOnly:e,disableUnderline:n,textFieldProps:t,autocompleteProps:a,...f})=>{const i=l.useMemo(()=>q({readOnly:e,disableClearable:e,disabled:e,slotProps:{input:{disableUnderline:n},chip:{disabled:!1}}},a,e?{sx:{".MuiAutocomplete-endAdornment":{display:"none"},".MuiAutocomplete-tag":{opacity:"1 !important"}}}:{}),[a,e,n]),c=l.useMemo(()=>q(e?{variant:"standard"}:{},t),[e,t]);return p.jsx($.AutocompleteElement,{...f,autocompleteProps:i,textFieldProps:c})};function J(e,n){return n.multiple,n?.freeSolo,e}const{get:L}=M;function P(e,n){const t=L(e,n);return{error:!!t,helperText:t?.message||""}}function S(e){const n=l.useRef(!1);l.useEffect(()=>(n.current||(e(),n.current=!0),()=>{n.current=!0}),[])}const{omit:g}=M,Q=({options:e,autocompleteProps:n,getItemKey:t,getItemLabel:a,getChipProps:f,stringToNewItem:i,name:c,freeSolo:d,control:F,...C})=>{const{field:j}=$.useController({name:c,control:F}),[E,b]=l.useState(""),[y,k]=l.useState([]);S(()=>{if(!d||!j.value)return;const r=(Array.isArray(j.value)?j.value:[j.value]).filter(s=>typeof s=="string"?!1:!e.some(u=>t(u)===t(s)));r.length>0&&k(r)});const h=l.useMemo(()=>{if(!d||!i||!E.length)return;const o=i(E),r=t(o);if(!e.some(s=>t(s)===r)&&!y.some(s=>t(s)===r))return o},[i,E,y,d,e,t]),H=l.useMemo(()=>{if(!d)return e;const o=[...e,...y];h&&o.push(h);const r=new Set;return o.filter(s=>{const u=t(s);return r.has(u)?!1:(r.add(u),!0)})},[e,y,d,h,t]);return p.jsx(D,{name:c,control:F,options:H,...C,autocompleteProps:{isOptionEqualToValue:(o,r)=>t(o)===t(r),filterOptions:(o,{inputValue:r})=>o.filter(s=>t(s).toLowerCase().includes(r.toLowerCase())),freeSolo:d,autoComplete:!0,autoHighlight:!0,openOnFocus:!0,renderOption:(o,r,{selected:s})=>l.createElement("li",{...o,key:`${c}-option-${t(r)}`},C?.showCheckbox&&p.jsx(x.Checkbox,{sx:{marginRight:1},checked:s}),typeof r=="string"?r:a(r)),onChange:(o,r,s,u)=>{if(d&&h){if(i==null)throw new Error("Must implement stringToNewItem with freeSolo!");k(A=>[...A,h]),b("")}n?.onChange?.(o,r,s,u)},onInputChange:(o,r,s)=>{b(r),n?.onInputChange?.(o,r,s)},renderValue:(o,r,s)=>{const u=J(o,s);if(Array.isArray(u))return u.map((m,V)=>{const{key:T,...w}=r({index:V}),z=typeof m=="string"?m:a(m),B=typeof m!="string"&&f?f({value:m,index:V}):{};return p.jsx(x.Chip,{label:z,...B,...w},`${c}-chip-${T}`)});const{key:A,...R}=r({index:0}),O=g(R,"onDelete");return p.jsx(x.Typography,{component:"span",color:"text.primary",...C?.viewOnly?g(O,"disabled"):O,children:typeof u=="string"?u:a(u)},`${c}-value-${A}`)},...n}})},W=({name:e,rules:n,formControlProps:t={}})=>{const{register:a,formState:{errors:f}}=G.useFormContext(),{error:i,helperText:c}=P(f,e);return p.jsxs(x.FormControl,{error:i,...t,children:[p.jsx("input",{type:"hidden",...a(e,n)}),i&&p.jsx(x.FormHelperText,{children:c})]})};exports.AutocompleteElementDisplay=D;exports.ObjectDisplayElement=Q;exports.ValidationElement=W;exports.useFormError=P;exports.useOnMount=S;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("react/jsx-runtime"),$=require("react-hook-form-mui"),l=require("react"),M=require("lodash"),x=require("@mui/material"),G=require("react-hook-form"),{merge:q}=M,D=({viewOnly:e,disableUnderline:n,textFieldProps:t,autocompleteProps:a,...f})=>{const i=l.useMemo(()=>q({readOnly:e,disableClearable:e,disabled:e,slotProps:{input:{disableUnderline:n},chip:{disabled:!1}}},a,e?{sx:{".MuiAutocomplete-endAdornment":{display:"none"},".MuiAutocomplete-tag":{opacity:"1 !important"}}}:{}),[a,e,n]),c=l.useMemo(()=>q(e?{variant:"standard"}:{},t),[e,t]);return p.jsx($.AutocompleteElement,{...f,autocompleteProps:i,textFieldProps:c})};function J(e,n){return n.multiple,n?.freeSolo,e}const{get:L}=M;function P(e,n){const t=L(e,n);return{error:!!t,helperText:t?.message||""}}function S(e){const n=l.useRef(!1);l.useEffect(()=>(n.current||(e(),n.current=!0),()=>{n.current=!0}),[])}const{omit:g}=M,Q=({options:e,autocompleteProps:n,getItemKey:t,getItemLabel:a,getChipProps:f,stringToNewItem:i,name:c,freeSolo:d,control:F,...C})=>{const{field:j}=$.useController({name:c,control:F}),[E,b]=l.useState(""),[y,k]=l.useState([]);S(()=>{if(!d||!j.value)return;const r=(Array.isArray(j.value)?j.value:[j.value]).filter(s=>typeof s=="string"?!1:!e.some(u=>t(u)===t(s)));r.length>0&&k(r)});const h=l.useMemo(()=>{if(!d||!i||!E.length)return;const o=i(E),r=t(o);if(!e.some(s=>t(s)===r)&&!y.some(s=>t(s)===r))return o},[i,E,y,d,e,t]),H=l.useMemo(()=>{if(!d)return e;const o=[...e,...y];h&&o.push(h);const r=new Set;return o.filter(s=>{const u=t(s);return r.has(u)?!1:(r.add(u),!0)})},[e,y,d,h,t]);return p.jsx(D,{name:c,control:F,options:H,...C,autocompleteProps:{isOptionEqualToValue:(o,r)=>t(o)===t(r),filterOptions:(o,{inputValue:r})=>o.filter(s=>t(s).toLowerCase().includes(r.toLowerCase())),freeSolo:d,autoComplete:!0,autoHighlight:!0,openOnFocus:!0,renderOption:(o,r,{selected:s})=>l.createElement("li",{...o,key:`${c}-option-${t(r)}`},C?.showCheckbox&&p.jsx(x.Checkbox,{sx:{marginRight:1},checked:s}),typeof r=="string"?r:a(r)),onChange:(o,r,s,u)=>{if(d&&h){if(i==null)throw new Error("Must implement stringToNewItem with freeSolo!");k(A=>[...A,h]),b("")}n?.onChange?.(o,r,s,u)},onInputChange:(o,r,s)=>{b(r),n?.onInputChange?.(o,r,s)},renderValue:(o,r,s)=>{const u=J(o,s);if(Array.isArray(u))return u.map((m,V)=>{const{key:T,...w}=r({index:V}),z=typeof m=="string"?m:a(m),B=typeof m!="string"&&f?f({value:m,index:V}):{};return p.jsx(x.Chip,{label:z,...B,...w},`${c}-chip-${T}`)});const{key:A,...R}=r({index:0}),O=g(R,"onDelete");return p.jsx(x.Typography,{component:"span",color:"text.primary",...C?.viewOnly?g(O,"disabled"):O,children:typeof u=="string"?u:a(u)},`${c}-value-${A}`)},...n}})},W=({name:e,rules:n,formControlProps:t={}})=>{const{register:a,formState:{errors:f}}=G.useFormContext(),{error:i,helperText:c}=P(f,e);return p.jsxs(x.FormControl,{error:i,...t,children:[p.jsx("input",{type:"hidden",...a(e,n)}),i&&p.jsx(x.FormHelperText,{children:c})]})};exports.AutocompleteElementDisplay=D;exports.ObjectElementDisplay=Q;exports.ValidationElement=W;exports.useFormError=P;exports.useOnMount=S;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/components/AutocompleteElementDisplay.tsx","../src/utils.ts","../src/hooks/useFormError.ts","../src/hooks/useOnMount.ts","../src/components/ObjectDisplayElement.tsx","../src/components/ValidationElement.tsx"],"sourcesContent":["import { AutocompleteElement, type AutocompleteElementProps } from \"react-hook-form-mui\";\r\nimport { type ChipTypeMap } from \"@mui/material\";\r\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\r\nimport { type ElementType, useMemo } from \"react\";\r\nimport lodash from \"lodash\";\r\nconst { merge } = lodash;\r\n\r\nexport type AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n> = AutocompleteElementProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n> &\r\n Viewable;\r\n\r\nexport const AutocompleteElementDisplay = <\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n>({\r\n viewOnly,\r\n disableUnderline,\r\n textFieldProps,\r\n autocompleteProps,\r\n ...props\r\n}: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n>) => {\r\n const autocompleteAdjustedProps: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n >['autocompleteProps'] = useMemo(\r\n () =>\r\n merge(\r\n {\r\n readOnly: viewOnly,\r\n disableClearable: viewOnly,\r\n disabled: viewOnly,\r\n slotProps: {\r\n input: { disableUnderline },\r\n chip: {\r\n disabled: false,\r\n },\r\n },\r\n },\r\n autocompleteProps,\r\n viewOnly\r\n ? {\r\n sx: {\r\n \".MuiAutocomplete-endAdornment\": {\r\n display: \"none\",\r\n },\r\n \".MuiAutocomplete-tag\": {\r\n opacity: \"1 !important\"\r\n },\r\n },\r\n }\r\n : {},\r\n ),\r\n [autocompleteProps, viewOnly, disableUnderline],\r\n );\r\n\r\n const textFieldAdjustedProps = useMemo(\r\n () => merge(viewOnly ? { variant: \"standard\" } : {}, textFieldProps),\r\n [viewOnly, textFieldProps],\r\n );\r\n\r\n return (\r\n <AutocompleteElement\r\n {...props}\r\n autocompleteProps={autocompleteAdjustedProps}\r\n textFieldProps={textFieldAdjustedProps}\r\n />\r\n );\r\n};\r\n\r\ntype Viewable = {\r\n viewOnly?: boolean;\r\n disableUnderline?: boolean;\r\n};\r\n","import type { AutocompleteOwnerState, AutocompleteRenderValue, ChipTypeMap } from \"@mui/material\";\r\nimport type { ElementType } from \"react\";\r\n\r\nexport function getAutocompleteRenderValue<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap['defaultComponent']\r\n>(value: AutocompleteRenderValue<TValue, Multiple, FreeSolo>, ownerState: AutocompleteOwnerState<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent>) {\r\n if (ownerState.multiple) {\r\n if(ownerState?.freeSolo) return value as Array<TValue | string>;\r\n return value as TValue[];\r\n } else if (ownerState?.freeSolo) {\r\n return value as NonNullable<TValue | string>;\r\n }\r\n return value as NonNullable<TValue>;\r\n}","import { FieldError, FieldErrors, FieldValues } from 'react-hook-form';\r\nimport lodash from 'lodash';\r\nconst { get } = lodash;\r\n\r\n/**\r\n * A hook to get the error message for a field from react-hook-form\r\n * @param errors The errors object from react-hook-form\r\n * @param name The name of the field (supports dot notation for nested fields)\r\n * @returns An object with error and helperText properties\r\n */\r\nexport function useFormError<T extends FieldValues>(\r\n errors: FieldErrors<T>,\r\n name: string\r\n): { error: boolean; helperText: string } {\r\n // Get the error for the field, supporting nested paths with dot notation\r\n const fieldError = get(errors, name) as FieldError | undefined;\r\n\r\n // Return error state and helper text\r\n return {\r\n error: !!fieldError,\r\n helperText: fieldError?.message || '',\r\n };\r\n}\r\n","// src/hooks/useOnMount.ts\r\nimport { useEffect, useRef } from \"react\";\r\n\r\n/**\r\n * Runs a provided callback function once on the first component mount.\r\n *\r\n * @param callback - The function to run on first mount.\r\n */\r\nexport function useOnMount(callback: () => void): void {\r\n const hasMounted = useRef(false);\r\n\r\n useEffect(() => {\r\n if (!hasMounted.current) {\r\n callback();\r\n hasMounted.current = true;\r\n }\r\n\r\n // Cleanup to prevent callback logic from running during cleanup\r\n return () => {\r\n hasMounted.current = true;\r\n };\r\n \r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n}\r\n\r\n","import { type ElementType, ReactNode, useMemo, useState } from \"react\";\nimport { Checkbox, Chip, Typography, type ChipProps, type ChipTypeMap } from \"@mui/material\";\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\nimport {\n AutocompleteElementDisplay,\n type AutocompleteElementDisplayProps,\n} from \"./AutocompleteElementDisplay\";\nimport { getAutocompleteRenderValue } from \"../utils\";\nimport { useController } from \"react-hook-form-mui\";\nimport { useOnMount } from \"../hooks\";\nimport lodash from \"lodash\";\nconst { omit } = lodash;\n\n/**\n * Extends DisplayAutocompleteProps with additional properties for handling object values.\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n */\nexport type DisplayObjectCompleteProps<\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = AutocompleteElementDisplayProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n> & {\n /**\n * Function to extract a unique key from an option value.\n * Used for option comparison and deduplication.\n *\n * @param value - The option value or null\n * @returns A unique string key for the value\n */\n getItemKey: (value: TValue | null) => string;\n\n /**\n * Function to generate a display label for an option value.\n * Can return any ReactNode for custom rendering.\n *\n * @param value - The option value or null\n * @returns A ReactNode to display as the option label\n */\n getItemLabel: (value: TValue | null) => ReactNode;\n\n /**\n * Function to convert a free text input string to a TValue object.\n * Required when freeSolo is true to create new items from text input.\n *\n * @param value - The string value entered by the user\n * @returns A new TValue object created from the string\n */\n stringToNewItem?: (value: string) => TValue;\n\n /**\n * Whether the input allows free text entry.\n * When true, users can enter values that are not in the options.\n */\n freeSolo?: FreeSolo;\n\n /**\n * Optional function that returns additional chip props based on the value.\n * This allows for customizing chip appearance and behavior based on the value it represents.\n *\n * @param value - The option value being rendered as a chip\n * @returns Additional props to apply to the Chip component\n */\n getChipProps?: (props: { value: TValue; index: number }) => Partial<ChipProps> | undefined;\n};\n\n/**\n * A form component that displays a searchable dropdown for selecting object values.\n * Extends AutocompleteDisplayElement with object-specific functionality.\n *\n * Features:\n * - Works with complex object values instead of just primitive types\n * - Supports both single and multiple selection modes\n * - Supports free-solo mode for creating new items from text input\n * - Handles initial values that aren't in the default options\n * - Deduplicates options based on item keys\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n *\n * @returns A React component for selecting object values\n */\nexport const ObjectDisplayElement = <\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n options,\n autocompleteProps,\n getItemKey,\n getItemLabel,\n getChipProps,\n stringToNewItem,\n name,\n freeSolo,\n control,\n ...props\n}: DisplayObjectCompleteProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n>) => {\n /**\n * Access to the form field controller\n */\n const { field } = useController({ name, control });\n\n /**\n * State for the current text input in free-solo mode\n */\n const [freeSoloValue, setFreeSoloValue] = useState(\"\");\n\n /**\n * State for storing dynamically added options that aren't in the original options list\n */\n const [newOptions, setNewOptions] = useState<TValue[]>([]);\n\n /**\n * On component mount, handle initial field values that aren't in the options\n * This is important for loading saved data that might reference items not in the current options\n */\n useOnMount(() => {\n if (!freeSolo || !field.value) return;\n\n // Handle both single and multiple selection modes\n const fieldValues: TValue[] = Array.isArray(field.value) ? field.value : [field.value];\n\n // Filter out values that are already in options\n const newFieldOptions = fieldValues.filter((value) => {\n // Skip string values as they're handled differently\n if (typeof value === \"string\") return false;\n\n // Check if this value exists in options\n return !options.some((option) => getItemKey(option) === getItemKey(value));\n });\n\n // Add new values to newOptions if any were found\n if (newFieldOptions.length > 0) setNewOptions(newFieldOptions);\n });\n\n /**\n * Creates a new item from the current free-solo text input\n * Returns undefined if:\n * - Free-solo mode is disabled\n * - No stringToNewItem converter is provided\n * - Input is empty\n * - An item with the same key already exists in options or newOptions\n *\n * @returns The new item created from the free-solo input, or undefined\n */\n const freeSoloItem = useMemo(() => {\n if (!freeSolo || !stringToNewItem || !freeSoloValue.length) return undefined;\n const item = stringToNewItem(freeSoloValue);\n const itemKey = getItemKey(item);\n if (options.some((option) => getItemKey(option) === itemKey)) return undefined;\n if (newOptions.some((option) => getItemKey(option) === itemKey)) return undefined;\n return item;\n }, [stringToNewItem, freeSoloValue, newOptions, freeSolo, options, getItemKey]);\n\n /**\n * Creates a combined and deduplicated list of all available options\n * Includes:\n * - Original options from props\n * - Dynamically added options from newOptions\n * - Current free-solo item if it exists\n *\n * @returns Array of all available options with duplicates removed\n */\n const allOptions = useMemo(() => {\n if (!freeSolo) return options;\n\n // Combine all options and deduplicate by key\n const combinedOptions = [...options, ...newOptions];\n if (freeSoloItem) combinedOptions.push(freeSoloItem);\n\n // Deduplicate using getItemKey\n const uniqueKeys = new Set();\n return combinedOptions.filter((option) => {\n const key = getItemKey(option);\n if (uniqueKeys.has(key)) return false;\n uniqueKeys.add(key);\n return true;\n });\n }, [options, newOptions, freeSolo, freeSoloItem, getItemKey]);\n\n // console.log({ allOptions });\n\n return (\n <AutocompleteElementDisplay\n name={name}\n control={control}\n options={allOptions}\n {...props}\n autocompleteProps={{\n /**\n * Determines if two options should be considered equal\n * Uses the getItemKey function to compare option values\n */\n isOptionEqualToValue: (o, v) => getItemKey(o) === getItemKey(v),\n\n /**\n * Filters options based on the input value\n * Checks if the option key contains the input value (case-insensitive)\n */\n filterOptions: (options, { inputValue }) =>\n options.filter((option) =>\n getItemKey(option).toLowerCase().includes(inputValue.toLowerCase()),\n ),\n freeSolo, // Allowed to enter own string value\n autoComplete: true,\n autoHighlight: true, // The first option is highlighted by default\n openOnFocus: true, // Opens the menu when tabbed into\n\n /**\n * Custom rendering for each option in the dropdown list\n * Displays a checkbox for multiple selection if showCheckbox is true\n * Uses getItemLabel to render the option label\n */\n renderOption: (liProps, option, { selected }) => (\n <li {...liProps} key={`${name}-option-${getItemKey(option)}`}>\n {props?.showCheckbox && <Checkbox sx={{ marginRight: 1 }} checked={selected} />}\n {typeof option === \"string\" ? option : getItemLabel(option)}\n </li>\n ),\n\n /**\n * Handles changes to the selected value(s)\n * In free-solo mode, adds the new item to newOptions when selected\n * Delegates to the original onChange handler if provided\n */\n onChange: (event, value, reason, details) => {\n if (freeSolo && freeSoloItem) {\n if (stringToNewItem == undefined) {\n throw new Error(\"Must implement stringToNewItem with freeSolo!\");\n }\n setNewOptions((prev) => [...prev, freeSoloItem]);\n setFreeSoloValue(\"\");\n }\n\n autocompleteProps?.onChange?.(event, value, reason, details);\n },\n\n /**\n * Handles changes to the input text\n * Updates freeSoloValue state with the current input\n * Delegates to the original onInputChange handler if provided\n */\n onInputChange: (event, value, reason) => {\n // event.preventDefault();\n setFreeSoloValue(value);\n\n // Call the original onInputChange if it exists\n autocompleteProps?.onInputChange?.(event, value, reason);\n },\n\n /**\n * Custom rendering for the selected value(s)\n * For multiple selection, renders a Chip for each selected value\n * For single selection, renders the value as text\n * Uses getItemLabel to render the value labels\n */\n renderValue: (value, getItemProps, ownerState) => {\n const typedValue = getAutocompleteRenderValue(value, ownerState);\n\n if (Array.isArray(typedValue)) {\n return typedValue.map((v, index) => {\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n const { key, ...chipProps } = getItemProps({ index });\n // const { key, ...rawChipProps } = getItemProps({ index });\n // const chipProps = omit(rawChipProps, \"onDelete\");\n\n const label = typeof v === \"string\" ? v : getItemLabel(v);\n\n // Get additional chip props based on the value if the function is provided\n const valueSpecificProps =\n typeof v !== \"string\" && getChipProps ? getChipProps({ value: v, index }) : {};\n return (\n <Chip\n key={`${name}-chip-${key}`}\n label={label}\n {...valueSpecificProps}\n {...chipProps}\n />\n );\n });\n }\n\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n // const { key, ...rawChipProps } = getItemProps({ index: 0 });\n const { key, ...rawChipProps } = getItemProps({ index: 0 });\n const itemProps = omit(rawChipProps, \"onDelete\");\n return (\n <Typography\n component={\"span\"}\n key={`${name}-value-${key}`}\n color={\"text.primary\"}\n {...(props?.viewOnly ? omit(itemProps, \"disabled\") : itemProps)}\n >\n {(typeof typedValue === \"string\") ? typedValue : getItemLabel(typedValue as NonNullable<TValue>)}\n </Typography>\n );\n },\n ...autocompleteProps,\n }}\n />\n );\n};\n","import type { JSX } from \"react\";\r\nimport { FormControl, FormHelperText } from \"@mui/material\";\r\nimport type { FormControlProps } from \"@mui/material\";\r\nimport { useFormContext, type RegisterOptions, type FieldValues, type Path } from \"react-hook-form\";\r\nimport { useFormError } from \"../hooks\";\r\n\r\n/**\r\n * Props for the RHFHiddenInput component\r\n */\r\nexport interface ValidationElementProps<TFieldValues extends FieldValues> {\r\n /**\r\n * Name of the field in the form\r\n */\r\n name: Path<TFieldValues>;\r\n /**\r\n * Optional validation rules\r\n */\r\n rules: RegisterOptions<TFieldValues>;\r\n /**\r\n * Props to pass to the FormControl component\r\n */\r\n formControlProps?: Omit<FormControlProps, \"error\">;\r\n}\r\n\r\nexport const ValidationElement = <TFieldValues extends FieldValues = FieldValues>({\r\n name,\r\n rules,\r\n formControlProps = {},\r\n}: ValidationElementProps<TFieldValues>): JSX.Element => {\r\n const {\r\n register,\r\n formState: { errors },\r\n } = useFormContext<TFieldValues>();\r\n\r\n const { error, helperText } = useFormError(errors, name);\r\n\r\n return (\r\n <FormControl error={error} {...formControlProps}>\r\n <input type=\"hidden\" {...register(name, rules)} />\r\n {error && <FormHelperText>{helperText}</FormHelperText>}\r\n </FormControl>\r\n );\r\n};\r\n"],"names":["merge","lodash","AutocompleteElementDisplay","viewOnly","disableUnderline","textFieldProps","autocompleteProps","props","autocompleteAdjustedProps","useMemo","textFieldAdjustedProps","jsx","AutocompleteElement","getAutocompleteRenderValue","value","ownerState","get","useFormError","errors","name","fieldError","useOnMount","callback","hasMounted","useRef","useEffect","omit","ObjectDisplayElement","options","getItemKey","getItemLabel","getChipProps","stringToNewItem","freeSolo","control","field","useController","freeSoloValue","setFreeSoloValue","useState","newOptions","setNewOptions","newFieldOptions","option","freeSoloItem","item","itemKey","allOptions","combinedOptions","uniqueKeys","key","v","inputValue","liProps","selected","createElement","Checkbox","event","reason","details","prev","getItemProps","typedValue","index","chipProps","label","valueSpecificProps","Chip","rawChipProps","itemProps","Typography","ValidationElement","rules","formControlProps","register","useFormContext","error","helperText","jsxs","FormControl","FormHelperText"],"mappings":"qPAKM,CAAE,MAAAA,GAAUC,EAqBLC,EAA6B,CAQxC,CACA,SAAAC,EACA,iBAAAC,EACA,eAAAC,EACA,kBAAAC,EACA,GAAGC,CACL,IAQM,CACJ,MAAMC,EAQmBC,EAAAA,QACvB,IACET,EACE,CACE,SAAUG,EACV,iBAAkBA,EAClB,SAAUA,EACV,UAAW,CACT,MAAO,CAAE,iBAAAC,CAAA,EACT,KAAM,CACJ,SAAU,EAAA,CACZ,CACF,EAEFE,EACAH,EACI,CACE,GAAI,CACF,gCAAiC,CAC/B,QAAS,MAAA,EAEX,uBAAwB,CACtB,QAAS,cAAA,CACX,CACF,EAEF,CAAA,CAAC,EAET,CAACG,EAAmBH,EAAUC,CAAgB,CAAA,EAG1CM,EAAyBD,EAAAA,QAC7B,IAAMT,EAAMG,EAAW,CAAE,QAAS,UAAA,EAAe,CAAA,EAAIE,CAAc,EACnE,CAACF,EAAUE,CAAc,CAAA,EAG3B,OACEM,EAAAA,IAACC,EAAAA,oBAAA,CACE,GAAGL,EACJ,kBAAmBC,EACnB,eAAgBE,CAAA,CAAA,CAGtB,ECjGO,SAASG,EAMdC,EAA4DC,EAAiG,CAC7J,OAAIA,EAAW,SACVA,GAAY,SAAiBD,CAMpC,CCfA,KAAM,CAAE,IAAAE,GAAQf,EAQT,SAASgB,EACdC,EACAC,EACwC,CAExC,MAAMC,EAAaJ,EAAIE,EAAQC,CAAI,EAGnC,MAAO,CACL,MAAO,CAAC,CAACC,EACT,WAAYA,GAAY,SAAW,EAAA,CAEvC,CCdO,SAASC,EAAWC,EAA4B,CACrD,MAAMC,EAAaC,EAAAA,OAAO,EAAK,EAE/BC,EAAAA,UAAU,KACHF,EAAW,UACdD,EAAA,EACAC,EAAW,QAAU,IAIhB,IAAM,CACXA,EAAW,QAAU,EACvB,GAGC,CAAA,CAAE,CACP,CCbA,KAAM,CAAE,KAAAG,GAASzB,EA8FJ0B,EAAuB,CAQlC,CACA,QAAAC,EACA,kBAAAtB,EACA,WAAAuB,EACA,aAAAC,EACA,aAAAC,EACA,gBAAAC,EACA,KAAAb,EACA,SAAAc,EACA,QAAAC,EACA,GAAG3B,CACL,IAQM,CAIJ,KAAM,CAAE,MAAA4B,CAAA,EAAUC,EAAAA,cAAc,CAAE,KAAAjB,EAAM,QAAAe,EAAS,EAK3C,CAACG,EAAeC,CAAgB,EAAIC,EAAAA,SAAS,EAAE,EAK/C,CAACC,EAAYC,CAAa,EAAIF,EAAAA,SAAmB,CAAA,CAAE,EAMzDlB,EAAW,IAAM,CACf,GAAI,CAACY,GAAY,CAACE,EAAM,MAAO,OAM/B,MAAMO,GAHwB,MAAM,QAAQP,EAAM,KAAK,EAAIA,EAAM,MAAQ,CAACA,EAAM,KAAK,GAGjD,OAAQrB,GAEtC,OAAOA,GAAU,SAAiB,GAG/B,CAACc,EAAQ,KAAMe,GAAWd,EAAWc,CAAM,IAAMd,EAAWf,CAAK,CAAC,CAC1E,EAGG4B,EAAgB,OAAS,GAAGD,EAAcC,CAAe,CAC/D,CAAC,EAYD,MAAME,EAAenC,EAAAA,QAAQ,IAAM,CACjC,GAAI,CAACwB,GAAY,CAACD,GAAmB,CAACK,EAAc,OAAQ,OAC5D,MAAMQ,EAAOb,EAAgBK,CAAa,EACpCS,EAAUjB,EAAWgB,CAAI,EAC/B,GAAI,CAAAjB,EAAQ,KAAMe,GAAWd,EAAWc,CAAM,IAAMG,CAAO,GACvD,CAAAN,EAAW,KAAMG,GAAWd,EAAWc,CAAM,IAAMG,CAAO,EAC9D,OAAOD,CACT,EAAG,CAACb,EAAiBK,EAAeG,EAAYP,EAAUL,EAASC,CAAU,CAAC,EAWxEkB,EAAatC,EAAAA,QAAQ,IAAM,CAC/B,GAAI,CAACwB,EAAU,OAAOL,EAGtB,MAAMoB,EAAkB,CAAC,GAAGpB,EAAS,GAAGY,CAAU,EAC9CI,GAAcI,EAAgB,KAAKJ,CAAY,EAGnD,MAAMK,MAAiB,IACvB,OAAOD,EAAgB,OAAQL,GAAW,CACxC,MAAMO,EAAMrB,EAAWc,CAAM,EAC7B,OAAIM,EAAW,IAAIC,CAAG,EAAU,IAChCD,EAAW,IAAIC,CAAG,EACX,GACT,CAAC,CACH,EAAG,CAACtB,EAASY,EAAYP,EAAUW,EAAcf,CAAU,CAAC,EAI5D,OACElB,EAAAA,IAACT,EAAA,CACC,KAAAiB,EACA,QAAAe,EACA,QAASa,EACR,GAAGxC,EACJ,kBAAmB,CAKjB,qBAAsB,CAAC,EAAG4C,IAAMtB,EAAW,CAAC,IAAMA,EAAWsB,CAAC,EAM9D,cAAe,CAACvB,EAAS,CAAE,WAAAwB,CAAA,IACzBxB,EAAQ,OAAQe,GACdd,EAAWc,CAAM,EAAE,cAAc,SAASS,EAAW,YAAA,CAAa,CAAA,EAEtE,SAAAnB,EACA,aAAc,GACd,cAAe,GACf,YAAa,GAOb,aAAc,CAACoB,EAASV,EAAQ,CAAE,SAAAW,KAChCC,EAAAA,cAAC,KAAA,CAAI,GAAGF,EAAS,IAAK,GAAGlC,CAAI,WAAWU,EAAWc,CAAM,CAAC,EAAA,EACvDpC,GAAO,cAAgBI,EAAAA,IAAC6C,EAAAA,SAAA,CAAS,GAAI,CAAE,YAAa,CAAA,EAAK,QAASF,CAAA,CAAU,EAC5E,OAAOX,GAAW,SAAWA,EAASb,EAAaa,CAAM,CAC5D,EAQF,SAAU,CAACc,EAAO3C,EAAO4C,EAAQC,IAAY,CAC3C,GAAI1B,GAAYW,EAAc,CAC5B,GAAIZ,GAAmB,KACrB,MAAM,IAAI,MAAM,+CAA+C,EAEjES,EAAemB,GAAS,CAAC,GAAGA,EAAMhB,CAAY,CAAC,EAC/CN,EAAiB,EAAE,CACrB,CAEAhC,GAAmB,WAAWmD,EAAO3C,EAAO4C,EAAQC,CAAO,CAC7D,EAOA,cAAe,CAACF,EAAO3C,EAAO4C,IAAW,CAEvCpB,EAAiBxB,CAAK,EAGtBR,GAAmB,gBAAgBmD,EAAO3C,EAAO4C,CAAM,CACzD,EAQA,YAAa,CAAC5C,EAAO+C,EAAc9C,IAAe,CAChD,MAAM+C,EAAajD,EAA2BC,EAAOC,CAAU,EAE/D,GAAI,MAAM,QAAQ+C,CAAU,EAC1B,OAAOA,EAAW,IAAI,CAACX,EAAGY,IAAU,CAElC,KAAM,CAAE,IAAAb,EAAK,GAAGc,GAAcH,EAAa,CAAE,MAAAE,EAAO,EAI9CE,EAAQ,OAAOd,GAAM,SAAWA,EAAIrB,EAAaqB,CAAC,EAGlDe,EACJ,OAAOf,GAAM,UAAYpB,EAAeA,EAAa,CAAE,MAAOoB,EAAG,MAAAY,CAAA,CAAO,EAAI,CAAA,EAC9E,OACEpD,EAAAA,IAACwD,EAAAA,KAAA,CAEC,MAAAF,EACC,GAAGC,EACH,GAAGF,CAAA,EAHC,GAAG7C,CAAI,SAAS+B,CAAG,EAAA,CAM9B,CAAC,EAKH,KAAM,CAAE,IAAAA,EAAK,GAAGkB,CAAA,EAAiBP,EAAa,CAAE,MAAO,EAAG,EACpDQ,EAAY3C,EAAK0C,EAAc,UAAU,EAC/C,OACEzD,EAAAA,IAAC2D,EAAAA,WAAA,CACC,UAAW,OAEX,MAAO,eACN,GAAI/D,GAAO,SAAWmB,EAAK2C,EAAW,UAAU,EAAIA,EAEnD,SAAA,OAAOP,GAAe,SAAYA,EAAahC,EAAagC,CAAiC,CAAA,EAJ1F,GAAG3C,CAAI,UAAU+B,CAAG,EAAA,CAO/B,EACA,GAAG5C,CAAA,CACL,CAAA,CAGN,ECzTaiE,EAAoB,CAAiD,CAChF,KAAApD,EACA,MAAAqD,EACA,iBAAAC,EAAmB,CAAA,CACrB,IAAyD,CACvD,KAAM,CACJ,SAAAC,EACA,UAAW,CAAE,OAAAxD,CAAA,CAAO,EAClByD,iBAAA,EAEE,CAAE,MAAAC,EAAO,WAAAC,CAAA,EAAe5D,EAAaC,EAAQC,CAAI,EAEvD,OACE2D,EAAAA,KAACC,EAAAA,YAAA,CAAY,MAAAH,EAAe,GAAGH,EAC7B,SAAA,CAAA9D,MAAC,SAAM,KAAK,SAAU,GAAG+D,EAASvD,EAAMqD,CAAK,EAAG,EAC/CI,GAASjE,EAAAA,IAACqE,EAAAA,eAAA,CAAgB,SAAAH,CAAA,CAAW,CAAA,EACxC,CAEJ"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/components/AutocompleteElementDisplay.tsx","../src/utils.ts","../src/hooks/useFormError.ts","../src/hooks/useOnMount.ts","../src/components/ObjectElementDisplay.tsx","../src/components/ValidationElement.tsx"],"sourcesContent":["import { AutocompleteElement, type AutocompleteElementProps } from \"react-hook-form-mui\";\r\nimport { type ChipTypeMap } from \"@mui/material\";\r\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\r\nimport { type ElementType, useMemo } from \"react\";\r\nimport lodash from \"lodash\";\r\nconst { merge } = lodash;\r\n\r\nexport type AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n> = AutocompleteElementProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n> &\r\n Viewable;\r\n\r\nexport const AutocompleteElementDisplay = <\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n>({\r\n viewOnly,\r\n disableUnderline,\r\n textFieldProps,\r\n autocompleteProps,\r\n ...props\r\n}: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n>) => {\r\n const autocompleteAdjustedProps: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n >['autocompleteProps'] = useMemo(\r\n () =>\r\n merge(\r\n {\r\n readOnly: viewOnly,\r\n disableClearable: viewOnly,\r\n disabled: viewOnly,\r\n slotProps: {\r\n input: { disableUnderline },\r\n chip: {\r\n disabled: false,\r\n },\r\n },\r\n },\r\n autocompleteProps,\r\n viewOnly\r\n ? {\r\n sx: {\r\n \".MuiAutocomplete-endAdornment\": {\r\n display: \"none\",\r\n },\r\n \".MuiAutocomplete-tag\": {\r\n opacity: \"1 !important\"\r\n },\r\n },\r\n }\r\n : {},\r\n ),\r\n [autocompleteProps, viewOnly, disableUnderline],\r\n );\r\n\r\n const textFieldAdjustedProps = useMemo(\r\n () => merge(viewOnly ? { variant: \"standard\" } : {}, textFieldProps),\r\n [viewOnly, textFieldProps],\r\n );\r\n\r\n return (\r\n <AutocompleteElement\r\n {...props}\r\n autocompleteProps={autocompleteAdjustedProps}\r\n textFieldProps={textFieldAdjustedProps}\r\n />\r\n );\r\n};\r\n\r\ntype Viewable = {\r\n viewOnly?: boolean;\r\n disableUnderline?: boolean;\r\n};\r\n","import type { AutocompleteOwnerState, AutocompleteRenderValue, ChipTypeMap } from \"@mui/material\";\r\nimport type { ElementType } from \"react\";\r\n\r\nexport function getAutocompleteRenderValue<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap['defaultComponent']\r\n>(value: AutocompleteRenderValue<TValue, Multiple, FreeSolo>, ownerState: AutocompleteOwnerState<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent>) {\r\n if (ownerState.multiple) {\r\n if(ownerState?.freeSolo) return value as Array<TValue | string>;\r\n return value as TValue[];\r\n } else if (ownerState?.freeSolo) {\r\n return value as NonNullable<TValue | string>;\r\n }\r\n return value as NonNullable<TValue>;\r\n}","import { FieldError, FieldErrors, FieldValues } from 'react-hook-form';\r\nimport lodash from 'lodash';\r\nconst { get } = lodash;\r\n\r\n/**\r\n * A hook to get the error message for a field from react-hook-form\r\n * @param errors The errors object from react-hook-form\r\n * @param name The name of the field (supports dot notation for nested fields)\r\n * @returns An object with error and helperText properties\r\n */\r\nexport function useFormError<T extends FieldValues>(\r\n errors: FieldErrors<T>,\r\n name: string\r\n): { error: boolean; helperText: string } {\r\n // Get the error for the field, supporting nested paths with dot notation\r\n const fieldError = get(errors, name) as FieldError | undefined;\r\n\r\n // Return error state and helper text\r\n return {\r\n error: !!fieldError,\r\n helperText: fieldError?.message || '',\r\n };\r\n}\r\n","// src/hooks/useOnMount.ts\r\nimport { useEffect, useRef } from \"react\";\r\n\r\n/**\r\n * Runs a provided callback function once on the first component mount.\r\n *\r\n * @param callback - The function to run on first mount.\r\n */\r\nexport function useOnMount(callback: () => void): void {\r\n const hasMounted = useRef(false);\r\n\r\n useEffect(() => {\r\n if (!hasMounted.current) {\r\n callback();\r\n hasMounted.current = true;\r\n }\r\n\r\n // Cleanup to prevent callback logic from running during cleanup\r\n return () => {\r\n hasMounted.current = true;\r\n };\r\n \r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n}\r\n\r\n","import { type ElementType, ReactNode, useMemo, useState } from \"react\";\nimport { Checkbox, Chip, Typography, type ChipProps, type ChipTypeMap } from \"@mui/material\";\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\nimport {\n AutocompleteElementDisplay,\n type AutocompleteElementDisplayProps,\n} from \"./AutocompleteElementDisplay\";\nimport { getAutocompleteRenderValue } from \"../utils\";\nimport { useController } from \"react-hook-form-mui\";\nimport { useOnMount } from \"../hooks\";\nimport lodash from \"lodash\";\nconst { omit } = lodash;\n\n/**\n * Extends AutocompleteElementDisplayProps with additional properties for handling object values.\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n */\nexport type ObjectElementDisplayProps<\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = AutocompleteElementDisplayProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n> & {\n /**\n * Function to extract a unique key from an option value.\n * Used for option comparison and deduplication.\n *\n * @param value - The option value or null\n * @returns A unique string key for the value\n */\n getItemKey: (value: TValue | null) => string;\n\n /**\n * Function to generate a display label for an option value.\n * Can return any ReactNode for custom rendering.\n *\n * @param value - The option value or null\n * @returns A ReactNode to display as the option label\n */\n getItemLabel: (value: TValue | null) => ReactNode;\n\n /**\n * Function to convert a free text input string to a TValue object.\n * Required when freeSolo is true to create new items from text input.\n *\n * @param value - The string value entered by the user\n * @returns A new TValue object created from the string\n */\n stringToNewItem?: (value: string) => TValue;\n\n /**\n * Whether the input allows free text entry.\n * When true, users can enter values that are not in the options.\n */\n freeSolo?: FreeSolo;\n\n /**\n * Optional function that returns additional chip props based on the value.\n * This allows for customizing chip appearance and behavior based on the value it represents.\n *\n * @param value - The option value being rendered as a chip\n * @returns Additional props to apply to the Chip component\n */\n getChipProps?: (props: { value: TValue; index: number }) => Partial<ChipProps> | undefined;\n};\n\n/**\n * A form component that displays a searchable dropdown for selecting object values.\n * Extends AutocompleteElementDisplay with object-specific functionality.\n *\n * Features:\n * - Works with complex object values instead of just primitive types\n * - Supports both single and multiple selection modes\n * - Supports free-solo mode for creating new items from text input\n * - Handles initial values that aren't in the default options\n * - Deduplicates options based on item keys\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n *\n * @returns A React component for selecting object values\n */\nexport const ObjectElementDisplay = <\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n options,\n autocompleteProps,\n getItemKey,\n getItemLabel,\n getChipProps,\n stringToNewItem,\n name,\n freeSolo,\n control,\n ...props\n}: ObjectElementDisplayProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n>) => {\n /**\n * Access to the form field controller\n */\n const { field } = useController({ name, control });\n\n /**\n * State for the current text input in free-solo mode\n */\n const [freeSoloValue, setFreeSoloValue] = useState(\"\");\n\n /**\n * State for storing dynamically added options that aren't in the original options list\n */\n const [newOptions, setNewOptions] = useState<TValue[]>([]);\n\n /**\n * On component mount, handle initial field values that aren't in the options\n * This is important for loading saved data that might reference items not in the current options\n */\n useOnMount(() => {\n if (!freeSolo || !field.value) return;\n\n // Handle both single and multiple selection modes\n const fieldValues: TValue[] = Array.isArray(field.value) ? field.value : [field.value];\n\n // Filter out values that are already in options\n const newFieldOptions = fieldValues.filter((value) => {\n // Skip string values as they're handled differently\n if (typeof value === \"string\") return false;\n\n // Check if this value exists in options\n return !options.some((option) => getItemKey(option) === getItemKey(value));\n });\n\n // Add new values to newOptions if any were found\n if (newFieldOptions.length > 0) setNewOptions(newFieldOptions);\n });\n\n /**\n * Creates a new item from the current free-solo text input\n * Returns undefined if:\n * - Free-solo mode is disabled\n * - No stringToNewItem converter is provided\n * - Input is empty\n * - An item with the same key already exists in options or newOptions\n *\n * @returns The new item created from the free-solo input, or undefined\n */\n const freeSoloItem = useMemo(() => {\n if (!freeSolo || !stringToNewItem || !freeSoloValue.length) return undefined;\n const item = stringToNewItem(freeSoloValue);\n const itemKey = getItemKey(item);\n if (options.some((option) => getItemKey(option) === itemKey)) return undefined;\n if (newOptions.some((option) => getItemKey(option) === itemKey)) return undefined;\n return item;\n }, [stringToNewItem, freeSoloValue, newOptions, freeSolo, options, getItemKey]);\n\n /**\n * Creates a combined and deduplicated list of all available options\n * Includes:\n * - Original options from props\n * - Dynamically added options from newOptions\n * - Current free-solo item if it exists\n *\n * @returns Array of all available options with duplicates removed\n */\n const allOptions = useMemo(() => {\n if (!freeSolo) return options;\n\n // Combine all options and deduplicate by key\n const combinedOptions = [...options, ...newOptions];\n if (freeSoloItem) combinedOptions.push(freeSoloItem);\n\n // Deduplicate using getItemKey\n const uniqueKeys = new Set();\n return combinedOptions.filter((option) => {\n const key = getItemKey(option);\n if (uniqueKeys.has(key)) return false;\n uniqueKeys.add(key);\n return true;\n });\n }, [options, newOptions, freeSolo, freeSoloItem, getItemKey]);\n\n // console.log({ allOptions });\n\n return (\n <AutocompleteElementDisplay\n name={name}\n control={control}\n options={allOptions}\n {...props}\n autocompleteProps={{\n /**\n * Determines if two options should be considered equal\n * Uses the getItemKey function to compare option values\n */\n isOptionEqualToValue: (o, v) => getItemKey(o) === getItemKey(v),\n\n /**\n * Filters options based on the input value\n * Checks if the option key contains the input value (case-insensitive)\n */\n filterOptions: (options, { inputValue }) =>\n options.filter((option) =>\n getItemKey(option).toLowerCase().includes(inputValue.toLowerCase()),\n ),\n freeSolo, // Allowed to enter own string value\n autoComplete: true,\n autoHighlight: true, // The first option is highlighted by default\n openOnFocus: true, // Opens the menu when tabbed into\n\n /**\n * Custom rendering for each option in the dropdown list\n * Displays a checkbox for multiple selection if showCheckbox is true\n * Uses getItemLabel to render the option label\n */\n renderOption: (liProps, option, { selected }) => (\n <li {...liProps} key={`${name}-option-${getItemKey(option)}`}>\n {props?.showCheckbox && <Checkbox sx={{ marginRight: 1 }} checked={selected} />}\n {typeof option === \"string\" ? option : getItemLabel(option)}\n </li>\n ),\n\n /**\n * Handles changes to the selected value(s)\n * In free-solo mode, adds the new item to newOptions when selected\n * Delegates to the original onChange handler if provided\n */\n onChange: (event, value, reason, details) => {\n if (freeSolo && freeSoloItem) {\n if (stringToNewItem == undefined) {\n throw new Error(\"Must implement stringToNewItem with freeSolo!\");\n }\n setNewOptions((prev) => [...prev, freeSoloItem]);\n setFreeSoloValue(\"\");\n }\n\n autocompleteProps?.onChange?.(event, value, reason, details);\n },\n\n /**\n * Handles changes to the input text\n * Updates freeSoloValue state with the current input\n * Delegates to the original onInputChange handler if provided\n */\n onInputChange: (event, value, reason) => {\n // event.preventDefault();\n setFreeSoloValue(value);\n\n // Call the original onInputChange if it exists\n autocompleteProps?.onInputChange?.(event, value, reason);\n },\n\n /**\n * Custom rendering for the selected value(s)\n * For multiple selection, renders a Chip for each selected value\n * For single selection, renders the value as text\n * Uses getItemLabel to render the value labels\n */\n renderValue: (value, getItemProps, ownerState) => {\n const typedValue = getAutocompleteRenderValue(value, ownerState);\n\n if (Array.isArray(typedValue)) {\n return typedValue.map((v, index) => {\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n const { key, ...chipProps } = getItemProps({ index });\n\n const label = typeof v === \"string\" ? v : getItemLabel(v);\n\n // Get additional chip props based on the value if the function is provided\n const valueSpecificProps =\n typeof v !== \"string\" && getChipProps ? getChipProps({ value: v, index }) : {};\n return (\n <Chip\n key={`${name}-chip-${key}`}\n label={label}\n {...valueSpecificProps}\n {...chipProps}\n />\n );\n });\n }\n\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n const { key, ...rawChipProps } = getItemProps({ index: 0 });\n const itemProps = omit(rawChipProps, \"onDelete\");\n return (\n <Typography\n component={\"span\"}\n key={`${name}-value-${key}`}\n color={\"text.primary\"}\n {...(props?.viewOnly ? omit(itemProps, \"disabled\") : itemProps)}\n >\n {(typeof typedValue === \"string\") ? typedValue : getItemLabel(typedValue as NonNullable<TValue>)}\n </Typography>\n );\n },\n ...autocompleteProps,\n }}\n />\n );\n};\n","import type { JSX } from \"react\";\r\nimport { FormControl, FormHelperText } from \"@mui/material\";\r\nimport type { FormControlProps } from \"@mui/material\";\r\nimport { useFormContext, type RegisterOptions, type FieldValues, type Path } from \"react-hook-form\";\r\nimport { useFormError } from \"../hooks\";\r\n\r\n/**\r\n * Props for the RHFHiddenInput component\r\n */\r\nexport interface ValidationElementProps<TFieldValues extends FieldValues> {\r\n /**\r\n * Name of the field in the form\r\n */\r\n name: Path<TFieldValues>;\r\n /**\r\n * Optional validation rules\r\n */\r\n rules: RegisterOptions<TFieldValues>;\r\n /**\r\n * Props to pass to the FormControl component\r\n */\r\n formControlProps?: Omit<FormControlProps, \"error\">;\r\n}\r\n\r\nexport const ValidationElement = <TFieldValues extends FieldValues = FieldValues>({\r\n name,\r\n rules,\r\n formControlProps = {},\r\n}: ValidationElementProps<TFieldValues>): JSX.Element => {\r\n const {\r\n register,\r\n formState: { errors },\r\n } = useFormContext<TFieldValues>();\r\n\r\n const { error, helperText } = useFormError(errors, name);\r\n\r\n return (\r\n <FormControl error={error} {...formControlProps}>\r\n <input type=\"hidden\" {...register(name, rules)} />\r\n {error && <FormHelperText>{helperText}</FormHelperText>}\r\n </FormControl>\r\n );\r\n};\r\n"],"names":["merge","lodash","AutocompleteElementDisplay","viewOnly","disableUnderline","textFieldProps","autocompleteProps","props","autocompleteAdjustedProps","useMemo","textFieldAdjustedProps","jsx","AutocompleteElement","getAutocompleteRenderValue","value","ownerState","get","useFormError","errors","name","fieldError","useOnMount","callback","hasMounted","useRef","useEffect","omit","ObjectElementDisplay","options","getItemKey","getItemLabel","getChipProps","stringToNewItem","freeSolo","control","field","useController","freeSoloValue","setFreeSoloValue","useState","newOptions","setNewOptions","newFieldOptions","option","freeSoloItem","item","itemKey","allOptions","combinedOptions","uniqueKeys","key","v","inputValue","liProps","selected","createElement","Checkbox","event","reason","details","prev","getItemProps","typedValue","index","chipProps","label","valueSpecificProps","Chip","rawChipProps","itemProps","Typography","ValidationElement","rules","formControlProps","register","useFormContext","error","helperText","jsxs","FormControl","FormHelperText"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -207,7 +207,7 @@ const { omit: j } = V, sr = ({
207
207
  };
208
208
  export {
209
209
  _ as AutocompleteElementDisplay,
210
- sr as ObjectDisplayElement,
210
+ sr as ObjectElementDisplay,
211
211
  ir as ValidationElement,
212
212
  v as useFormError,
213
213
  K as useOnMount
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/components/AutocompleteElementDisplay.tsx","../src/utils.ts","../src/hooks/useFormError.ts","../src/hooks/useOnMount.ts","../src/components/ObjectDisplayElement.tsx","../src/components/ValidationElement.tsx"],"sourcesContent":["import { AutocompleteElement, type AutocompleteElementProps } from \"react-hook-form-mui\";\r\nimport { type ChipTypeMap } from \"@mui/material\";\r\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\r\nimport { type ElementType, useMemo } from \"react\";\r\nimport lodash from \"lodash\";\r\nconst { merge } = lodash;\r\n\r\nexport type AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n> = AutocompleteElementProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n> &\r\n Viewable;\r\n\r\nexport const AutocompleteElementDisplay = <\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n>({\r\n viewOnly,\r\n disableUnderline,\r\n textFieldProps,\r\n autocompleteProps,\r\n ...props\r\n}: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n>) => {\r\n const autocompleteAdjustedProps: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n >['autocompleteProps'] = useMemo(\r\n () =>\r\n merge(\r\n {\r\n readOnly: viewOnly,\r\n disableClearable: viewOnly,\r\n disabled: viewOnly,\r\n slotProps: {\r\n input: { disableUnderline },\r\n chip: {\r\n disabled: false,\r\n },\r\n },\r\n },\r\n autocompleteProps,\r\n viewOnly\r\n ? {\r\n sx: {\r\n \".MuiAutocomplete-endAdornment\": {\r\n display: \"none\",\r\n },\r\n \".MuiAutocomplete-tag\": {\r\n opacity: \"1 !important\"\r\n },\r\n },\r\n }\r\n : {},\r\n ),\r\n [autocompleteProps, viewOnly, disableUnderline],\r\n );\r\n\r\n const textFieldAdjustedProps = useMemo(\r\n () => merge(viewOnly ? { variant: \"standard\" } : {}, textFieldProps),\r\n [viewOnly, textFieldProps],\r\n );\r\n\r\n return (\r\n <AutocompleteElement\r\n {...props}\r\n autocompleteProps={autocompleteAdjustedProps}\r\n textFieldProps={textFieldAdjustedProps}\r\n />\r\n );\r\n};\r\n\r\ntype Viewable = {\r\n viewOnly?: boolean;\r\n disableUnderline?: boolean;\r\n};\r\n","import type { AutocompleteOwnerState, AutocompleteRenderValue, ChipTypeMap } from \"@mui/material\";\r\nimport type { ElementType } from \"react\";\r\n\r\nexport function getAutocompleteRenderValue<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap['defaultComponent']\r\n>(value: AutocompleteRenderValue<TValue, Multiple, FreeSolo>, ownerState: AutocompleteOwnerState<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent>) {\r\n if (ownerState.multiple) {\r\n if(ownerState?.freeSolo) return value as Array<TValue | string>;\r\n return value as TValue[];\r\n } else if (ownerState?.freeSolo) {\r\n return value as NonNullable<TValue | string>;\r\n }\r\n return value as NonNullable<TValue>;\r\n}","import { FieldError, FieldErrors, FieldValues } from 'react-hook-form';\r\nimport lodash from 'lodash';\r\nconst { get } = lodash;\r\n\r\n/**\r\n * A hook to get the error message for a field from react-hook-form\r\n * @param errors The errors object from react-hook-form\r\n * @param name The name of the field (supports dot notation for nested fields)\r\n * @returns An object with error and helperText properties\r\n */\r\nexport function useFormError<T extends FieldValues>(\r\n errors: FieldErrors<T>,\r\n name: string\r\n): { error: boolean; helperText: string } {\r\n // Get the error for the field, supporting nested paths with dot notation\r\n const fieldError = get(errors, name) as FieldError | undefined;\r\n\r\n // Return error state and helper text\r\n return {\r\n error: !!fieldError,\r\n helperText: fieldError?.message || '',\r\n };\r\n}\r\n","// src/hooks/useOnMount.ts\r\nimport { useEffect, useRef } from \"react\";\r\n\r\n/**\r\n * Runs a provided callback function once on the first component mount.\r\n *\r\n * @param callback - The function to run on first mount.\r\n */\r\nexport function useOnMount(callback: () => void): void {\r\n const hasMounted = useRef(false);\r\n\r\n useEffect(() => {\r\n if (!hasMounted.current) {\r\n callback();\r\n hasMounted.current = true;\r\n }\r\n\r\n // Cleanup to prevent callback logic from running during cleanup\r\n return () => {\r\n hasMounted.current = true;\r\n };\r\n \r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n}\r\n\r\n","import { type ElementType, ReactNode, useMemo, useState } from \"react\";\nimport { Checkbox, Chip, Typography, type ChipProps, type ChipTypeMap } from \"@mui/material\";\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\nimport {\n AutocompleteElementDisplay,\n type AutocompleteElementDisplayProps,\n} from \"./AutocompleteElementDisplay\";\nimport { getAutocompleteRenderValue } from \"../utils\";\nimport { useController } from \"react-hook-form-mui\";\nimport { useOnMount } from \"../hooks\";\nimport lodash from \"lodash\";\nconst { omit } = lodash;\n\n/**\n * Extends DisplayAutocompleteProps with additional properties for handling object values.\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n */\nexport type DisplayObjectCompleteProps<\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = AutocompleteElementDisplayProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n> & {\n /**\n * Function to extract a unique key from an option value.\n * Used for option comparison and deduplication.\n *\n * @param value - The option value or null\n * @returns A unique string key for the value\n */\n getItemKey: (value: TValue | null) => string;\n\n /**\n * Function to generate a display label for an option value.\n * Can return any ReactNode for custom rendering.\n *\n * @param value - The option value or null\n * @returns A ReactNode to display as the option label\n */\n getItemLabel: (value: TValue | null) => ReactNode;\n\n /**\n * Function to convert a free text input string to a TValue object.\n * Required when freeSolo is true to create new items from text input.\n *\n * @param value - The string value entered by the user\n * @returns A new TValue object created from the string\n */\n stringToNewItem?: (value: string) => TValue;\n\n /**\n * Whether the input allows free text entry.\n * When true, users can enter values that are not in the options.\n */\n freeSolo?: FreeSolo;\n\n /**\n * Optional function that returns additional chip props based on the value.\n * This allows for customizing chip appearance and behavior based on the value it represents.\n *\n * @param value - The option value being rendered as a chip\n * @returns Additional props to apply to the Chip component\n */\n getChipProps?: (props: { value: TValue; index: number }) => Partial<ChipProps> | undefined;\n};\n\n/**\n * A form component that displays a searchable dropdown for selecting object values.\n * Extends AutocompleteDisplayElement with object-specific functionality.\n *\n * Features:\n * - Works with complex object values instead of just primitive types\n * - Supports both single and multiple selection modes\n * - Supports free-solo mode for creating new items from text input\n * - Handles initial values that aren't in the default options\n * - Deduplicates options based on item keys\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n *\n * @returns A React component for selecting object values\n */\nexport const ObjectDisplayElement = <\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n options,\n autocompleteProps,\n getItemKey,\n getItemLabel,\n getChipProps,\n stringToNewItem,\n name,\n freeSolo,\n control,\n ...props\n}: DisplayObjectCompleteProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n>) => {\n /**\n * Access to the form field controller\n */\n const { field } = useController({ name, control });\n\n /**\n * State for the current text input in free-solo mode\n */\n const [freeSoloValue, setFreeSoloValue] = useState(\"\");\n\n /**\n * State for storing dynamically added options that aren't in the original options list\n */\n const [newOptions, setNewOptions] = useState<TValue[]>([]);\n\n /**\n * On component mount, handle initial field values that aren't in the options\n * This is important for loading saved data that might reference items not in the current options\n */\n useOnMount(() => {\n if (!freeSolo || !field.value) return;\n\n // Handle both single and multiple selection modes\n const fieldValues: TValue[] = Array.isArray(field.value) ? field.value : [field.value];\n\n // Filter out values that are already in options\n const newFieldOptions = fieldValues.filter((value) => {\n // Skip string values as they're handled differently\n if (typeof value === \"string\") return false;\n\n // Check if this value exists in options\n return !options.some((option) => getItemKey(option) === getItemKey(value));\n });\n\n // Add new values to newOptions if any were found\n if (newFieldOptions.length > 0) setNewOptions(newFieldOptions);\n });\n\n /**\n * Creates a new item from the current free-solo text input\n * Returns undefined if:\n * - Free-solo mode is disabled\n * - No stringToNewItem converter is provided\n * - Input is empty\n * - An item with the same key already exists in options or newOptions\n *\n * @returns The new item created from the free-solo input, or undefined\n */\n const freeSoloItem = useMemo(() => {\n if (!freeSolo || !stringToNewItem || !freeSoloValue.length) return undefined;\n const item = stringToNewItem(freeSoloValue);\n const itemKey = getItemKey(item);\n if (options.some((option) => getItemKey(option) === itemKey)) return undefined;\n if (newOptions.some((option) => getItemKey(option) === itemKey)) return undefined;\n return item;\n }, [stringToNewItem, freeSoloValue, newOptions, freeSolo, options, getItemKey]);\n\n /**\n * Creates a combined and deduplicated list of all available options\n * Includes:\n * - Original options from props\n * - Dynamically added options from newOptions\n * - Current free-solo item if it exists\n *\n * @returns Array of all available options with duplicates removed\n */\n const allOptions = useMemo(() => {\n if (!freeSolo) return options;\n\n // Combine all options and deduplicate by key\n const combinedOptions = [...options, ...newOptions];\n if (freeSoloItem) combinedOptions.push(freeSoloItem);\n\n // Deduplicate using getItemKey\n const uniqueKeys = new Set();\n return combinedOptions.filter((option) => {\n const key = getItemKey(option);\n if (uniqueKeys.has(key)) return false;\n uniqueKeys.add(key);\n return true;\n });\n }, [options, newOptions, freeSolo, freeSoloItem, getItemKey]);\n\n // console.log({ allOptions });\n\n return (\n <AutocompleteElementDisplay\n name={name}\n control={control}\n options={allOptions}\n {...props}\n autocompleteProps={{\n /**\n * Determines if two options should be considered equal\n * Uses the getItemKey function to compare option values\n */\n isOptionEqualToValue: (o, v) => getItemKey(o) === getItemKey(v),\n\n /**\n * Filters options based on the input value\n * Checks if the option key contains the input value (case-insensitive)\n */\n filterOptions: (options, { inputValue }) =>\n options.filter((option) =>\n getItemKey(option).toLowerCase().includes(inputValue.toLowerCase()),\n ),\n freeSolo, // Allowed to enter own string value\n autoComplete: true,\n autoHighlight: true, // The first option is highlighted by default\n openOnFocus: true, // Opens the menu when tabbed into\n\n /**\n * Custom rendering for each option in the dropdown list\n * Displays a checkbox for multiple selection if showCheckbox is true\n * Uses getItemLabel to render the option label\n */\n renderOption: (liProps, option, { selected }) => (\n <li {...liProps} key={`${name}-option-${getItemKey(option)}`}>\n {props?.showCheckbox && <Checkbox sx={{ marginRight: 1 }} checked={selected} />}\n {typeof option === \"string\" ? option : getItemLabel(option)}\n </li>\n ),\n\n /**\n * Handles changes to the selected value(s)\n * In free-solo mode, adds the new item to newOptions when selected\n * Delegates to the original onChange handler if provided\n */\n onChange: (event, value, reason, details) => {\n if (freeSolo && freeSoloItem) {\n if (stringToNewItem == undefined) {\n throw new Error(\"Must implement stringToNewItem with freeSolo!\");\n }\n setNewOptions((prev) => [...prev, freeSoloItem]);\n setFreeSoloValue(\"\");\n }\n\n autocompleteProps?.onChange?.(event, value, reason, details);\n },\n\n /**\n * Handles changes to the input text\n * Updates freeSoloValue state with the current input\n * Delegates to the original onInputChange handler if provided\n */\n onInputChange: (event, value, reason) => {\n // event.preventDefault();\n setFreeSoloValue(value);\n\n // Call the original onInputChange if it exists\n autocompleteProps?.onInputChange?.(event, value, reason);\n },\n\n /**\n * Custom rendering for the selected value(s)\n * For multiple selection, renders a Chip for each selected value\n * For single selection, renders the value as text\n * Uses getItemLabel to render the value labels\n */\n renderValue: (value, getItemProps, ownerState) => {\n const typedValue = getAutocompleteRenderValue(value, ownerState);\n\n if (Array.isArray(typedValue)) {\n return typedValue.map((v, index) => {\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n const { key, ...chipProps } = getItemProps({ index });\n // const { key, ...rawChipProps } = getItemProps({ index });\n // const chipProps = omit(rawChipProps, \"onDelete\");\n\n const label = typeof v === \"string\" ? v : getItemLabel(v);\n\n // Get additional chip props based on the value if the function is provided\n const valueSpecificProps =\n typeof v !== \"string\" && getChipProps ? getChipProps({ value: v, index }) : {};\n return (\n <Chip\n key={`${name}-chip-${key}`}\n label={label}\n {...valueSpecificProps}\n {...chipProps}\n />\n );\n });\n }\n\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n // const { key, ...rawChipProps } = getItemProps({ index: 0 });\n const { key, ...rawChipProps } = getItemProps({ index: 0 });\n const itemProps = omit(rawChipProps, \"onDelete\");\n return (\n <Typography\n component={\"span\"}\n key={`${name}-value-${key}`}\n color={\"text.primary\"}\n {...(props?.viewOnly ? omit(itemProps, \"disabled\") : itemProps)}\n >\n {(typeof typedValue === \"string\") ? typedValue : getItemLabel(typedValue as NonNullable<TValue>)}\n </Typography>\n );\n },\n ...autocompleteProps,\n }}\n />\n );\n};\n","import type { JSX } from \"react\";\r\nimport { FormControl, FormHelperText } from \"@mui/material\";\r\nimport type { FormControlProps } from \"@mui/material\";\r\nimport { useFormContext, type RegisterOptions, type FieldValues, type Path } from \"react-hook-form\";\r\nimport { useFormError } from \"../hooks\";\r\n\r\n/**\r\n * Props for the RHFHiddenInput component\r\n */\r\nexport interface ValidationElementProps<TFieldValues extends FieldValues> {\r\n /**\r\n * Name of the field in the form\r\n */\r\n name: Path<TFieldValues>;\r\n /**\r\n * Optional validation rules\r\n */\r\n rules: RegisterOptions<TFieldValues>;\r\n /**\r\n * Props to pass to the FormControl component\r\n */\r\n formControlProps?: Omit<FormControlProps, \"error\">;\r\n}\r\n\r\nexport const ValidationElement = <TFieldValues extends FieldValues = FieldValues>({\r\n name,\r\n rules,\r\n formControlProps = {},\r\n}: ValidationElementProps<TFieldValues>): JSX.Element => {\r\n const {\r\n register,\r\n formState: { errors },\r\n } = useFormContext<TFieldValues>();\r\n\r\n const { error, helperText } = useFormError(errors, name);\r\n\r\n return (\r\n <FormControl error={error} {...formControlProps}>\r\n <input type=\"hidden\" {...register(name, rules)} />\r\n {error && <FormHelperText>{helperText}</FormHelperText>}\r\n </FormControl>\r\n );\r\n};\r\n"],"names":["merge","lodash","AutocompleteElementDisplay","viewOnly","disableUnderline","textFieldProps","autocompleteProps","props","autocompleteAdjustedProps","useMemo","textFieldAdjustedProps","jsx","AutocompleteElement","getAutocompleteRenderValue","value","ownerState","get","useFormError","errors","name","fieldError","useOnMount","callback","hasMounted","useRef","useEffect","omit","ObjectDisplayElement","options","getItemKey","getItemLabel","getChipProps","stringToNewItem","freeSolo","control","field","useController","freeSoloValue","setFreeSoloValue","useState","newOptions","setNewOptions","newFieldOptions","option","freeSoloItem","item","itemKey","allOptions","combinedOptions","uniqueKeys","key","o","v","inputValue","liProps","selected","createElement","Checkbox","event","reason","details","prev","getItemProps","typedValue","index","chipProps","label","valueSpecificProps","Chip","rawChipProps","itemProps","Typography","ValidationElement","rules","formControlProps","register","useFormContext","error","helperText","jsxs","FormControl","FormHelperText"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/components/AutocompleteElementDisplay.tsx","../src/utils.ts","../src/hooks/useFormError.ts","../src/hooks/useOnMount.ts","../src/components/ObjectElementDisplay.tsx","../src/components/ValidationElement.tsx"],"sourcesContent":["import { AutocompleteElement, type AutocompleteElementProps } from \"react-hook-form-mui\";\r\nimport { type ChipTypeMap } from \"@mui/material\";\r\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\r\nimport { type ElementType, useMemo } from \"react\";\r\nimport lodash from \"lodash\";\r\nconst { merge } = lodash;\r\n\r\nexport type AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n> = AutocompleteElementProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n> &\r\n Viewable;\r\n\r\nexport const AutocompleteElementDisplay = <\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\r\n TFieldValues extends FieldValues = FieldValues,\r\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\r\n>({\r\n viewOnly,\r\n disableUnderline,\r\n textFieldProps,\r\n autocompleteProps,\r\n ...props\r\n}: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n>) => {\r\n const autocompleteAdjustedProps: AutocompleteElementDisplayProps<\r\n TValue,\r\n Multiple,\r\n DisableClearable,\r\n FreeSolo,\r\n ChipComponent,\r\n TFieldValues,\r\n TName\r\n >['autocompleteProps'] = useMemo(\r\n () =>\r\n merge(\r\n {\r\n readOnly: viewOnly,\r\n disableClearable: viewOnly,\r\n disabled: viewOnly,\r\n slotProps: {\r\n input: { disableUnderline },\r\n chip: {\r\n disabled: false,\r\n },\r\n },\r\n },\r\n autocompleteProps,\r\n viewOnly\r\n ? {\r\n sx: {\r\n \".MuiAutocomplete-endAdornment\": {\r\n display: \"none\",\r\n },\r\n \".MuiAutocomplete-tag\": {\r\n opacity: \"1 !important\"\r\n },\r\n },\r\n }\r\n : {},\r\n ),\r\n [autocompleteProps, viewOnly, disableUnderline],\r\n );\r\n\r\n const textFieldAdjustedProps = useMemo(\r\n () => merge(viewOnly ? { variant: \"standard\" } : {}, textFieldProps),\r\n [viewOnly, textFieldProps],\r\n );\r\n\r\n return (\r\n <AutocompleteElement\r\n {...props}\r\n autocompleteProps={autocompleteAdjustedProps}\r\n textFieldProps={textFieldAdjustedProps}\r\n />\r\n );\r\n};\r\n\r\ntype Viewable = {\r\n viewOnly?: boolean;\r\n disableUnderline?: boolean;\r\n};\r\n","import type { AutocompleteOwnerState, AutocompleteRenderValue, ChipTypeMap } from \"@mui/material\";\r\nimport type { ElementType } from \"react\";\r\n\r\nexport function getAutocompleteRenderValue<\r\n TValue,\r\n Multiple extends boolean | undefined,\r\n DisableClearable extends boolean | undefined,\r\n FreeSolo extends boolean | undefined,\r\n ChipComponent extends ElementType = ChipTypeMap['defaultComponent']\r\n>(value: AutocompleteRenderValue<TValue, Multiple, FreeSolo>, ownerState: AutocompleteOwnerState<TValue, Multiple, DisableClearable, FreeSolo, ChipComponent>) {\r\n if (ownerState.multiple) {\r\n if(ownerState?.freeSolo) return value as Array<TValue | string>;\r\n return value as TValue[];\r\n } else if (ownerState?.freeSolo) {\r\n return value as NonNullable<TValue | string>;\r\n }\r\n return value as NonNullable<TValue>;\r\n}","import { FieldError, FieldErrors, FieldValues } from 'react-hook-form';\r\nimport lodash from 'lodash';\r\nconst { get } = lodash;\r\n\r\n/**\r\n * A hook to get the error message for a field from react-hook-form\r\n * @param errors The errors object from react-hook-form\r\n * @param name The name of the field (supports dot notation for nested fields)\r\n * @returns An object with error and helperText properties\r\n */\r\nexport function useFormError<T extends FieldValues>(\r\n errors: FieldErrors<T>,\r\n name: string\r\n): { error: boolean; helperText: string } {\r\n // Get the error for the field, supporting nested paths with dot notation\r\n const fieldError = get(errors, name) as FieldError | undefined;\r\n\r\n // Return error state and helper text\r\n return {\r\n error: !!fieldError,\r\n helperText: fieldError?.message || '',\r\n };\r\n}\r\n","// src/hooks/useOnMount.ts\r\nimport { useEffect, useRef } from \"react\";\r\n\r\n/**\r\n * Runs a provided callback function once on the first component mount.\r\n *\r\n * @param callback - The function to run on first mount.\r\n */\r\nexport function useOnMount(callback: () => void): void {\r\n const hasMounted = useRef(false);\r\n\r\n useEffect(() => {\r\n if (!hasMounted.current) {\r\n callback();\r\n hasMounted.current = true;\r\n }\r\n\r\n // Cleanup to prevent callback logic from running during cleanup\r\n return () => {\r\n hasMounted.current = true;\r\n };\r\n \r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n}\r\n\r\n","import { type ElementType, ReactNode, useMemo, useState } from \"react\";\nimport { Checkbox, Chip, Typography, type ChipProps, type ChipTypeMap } from \"@mui/material\";\nimport type { FieldPath, FieldValues } from \"react-hook-form\";\nimport {\n AutocompleteElementDisplay,\n type AutocompleteElementDisplayProps,\n} from \"./AutocompleteElementDisplay\";\nimport { getAutocompleteRenderValue } from \"../utils\";\nimport { useController } from \"react-hook-form-mui\";\nimport { useOnMount } from \"../hooks\";\nimport lodash from \"lodash\";\nconst { omit } = lodash;\n\n/**\n * Extends AutocompleteElementDisplayProps with additional properties for handling object values.\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n */\nexport type ObjectElementDisplayProps<\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = AutocompleteElementDisplayProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n> & {\n /**\n * Function to extract a unique key from an option value.\n * Used for option comparison and deduplication.\n *\n * @param value - The option value or null\n * @returns A unique string key for the value\n */\n getItemKey: (value: TValue | null) => string;\n\n /**\n * Function to generate a display label for an option value.\n * Can return any ReactNode for custom rendering.\n *\n * @param value - The option value or null\n * @returns A ReactNode to display as the option label\n */\n getItemLabel: (value: TValue | null) => ReactNode;\n\n /**\n * Function to convert a free text input string to a TValue object.\n * Required when freeSolo is true to create new items from text input.\n *\n * @param value - The string value entered by the user\n * @returns A new TValue object created from the string\n */\n stringToNewItem?: (value: string) => TValue;\n\n /**\n * Whether the input allows free text entry.\n * When true, users can enter values that are not in the options.\n */\n freeSolo?: FreeSolo;\n\n /**\n * Optional function that returns additional chip props based on the value.\n * This allows for customizing chip appearance and behavior based on the value it represents.\n *\n * @param value - The option value being rendered as a chip\n * @returns Additional props to apply to the Chip component\n */\n getChipProps?: (props: { value: TValue; index: number }) => Partial<ChipProps> | undefined;\n};\n\n/**\n * A form component that displays a searchable dropdown for selecting object values.\n * Extends AutocompleteElementDisplay with object-specific functionality.\n *\n * Features:\n * - Works with complex object values instead of just primitive types\n * - Supports both single and multiple selection modes\n * - Supports free-solo mode for creating new items from text input\n * - Handles initial values that aren't in the default options\n * - Deduplicates options based on item keys\n *\n * @template TValue - The type of the option values\n * @template Multiple - Boolean flag indicating if multiple selections are allowed\n * @template DisableClearable - Boolean flag indicating if clearing the selection is disabled\n * @template FreeSolo - Boolean flag indicating if free text input is allowed\n * @template ChipComponent - The component type used for rendering chips in multiple selection mode\n * @template TFieldValues - The type of the form values\n * @template TName - The type of the field name\n *\n * @returns A React component for selecting object values\n */\nexport const ObjectElementDisplay = <\n TValue,\n Multiple extends boolean | undefined,\n DisableClearable extends boolean | undefined,\n FreeSolo extends boolean | undefined,\n ChipComponent extends ElementType = ChipTypeMap[\"defaultComponent\"],\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n options,\n autocompleteProps,\n getItemKey,\n getItemLabel,\n getChipProps,\n stringToNewItem,\n name,\n freeSolo,\n control,\n ...props\n}: ObjectElementDisplayProps<\n TValue,\n Multiple,\n DisableClearable,\n FreeSolo,\n ChipComponent,\n TFieldValues,\n TName\n>) => {\n /**\n * Access to the form field controller\n */\n const { field } = useController({ name, control });\n\n /**\n * State for the current text input in free-solo mode\n */\n const [freeSoloValue, setFreeSoloValue] = useState(\"\");\n\n /**\n * State for storing dynamically added options that aren't in the original options list\n */\n const [newOptions, setNewOptions] = useState<TValue[]>([]);\n\n /**\n * On component mount, handle initial field values that aren't in the options\n * This is important for loading saved data that might reference items not in the current options\n */\n useOnMount(() => {\n if (!freeSolo || !field.value) return;\n\n // Handle both single and multiple selection modes\n const fieldValues: TValue[] = Array.isArray(field.value) ? field.value : [field.value];\n\n // Filter out values that are already in options\n const newFieldOptions = fieldValues.filter((value) => {\n // Skip string values as they're handled differently\n if (typeof value === \"string\") return false;\n\n // Check if this value exists in options\n return !options.some((option) => getItemKey(option) === getItemKey(value));\n });\n\n // Add new values to newOptions if any were found\n if (newFieldOptions.length > 0) setNewOptions(newFieldOptions);\n });\n\n /**\n * Creates a new item from the current free-solo text input\n * Returns undefined if:\n * - Free-solo mode is disabled\n * - No stringToNewItem converter is provided\n * - Input is empty\n * - An item with the same key already exists in options or newOptions\n *\n * @returns The new item created from the free-solo input, or undefined\n */\n const freeSoloItem = useMemo(() => {\n if (!freeSolo || !stringToNewItem || !freeSoloValue.length) return undefined;\n const item = stringToNewItem(freeSoloValue);\n const itemKey = getItemKey(item);\n if (options.some((option) => getItemKey(option) === itemKey)) return undefined;\n if (newOptions.some((option) => getItemKey(option) === itemKey)) return undefined;\n return item;\n }, [stringToNewItem, freeSoloValue, newOptions, freeSolo, options, getItemKey]);\n\n /**\n * Creates a combined and deduplicated list of all available options\n * Includes:\n * - Original options from props\n * - Dynamically added options from newOptions\n * - Current free-solo item if it exists\n *\n * @returns Array of all available options with duplicates removed\n */\n const allOptions = useMemo(() => {\n if (!freeSolo) return options;\n\n // Combine all options and deduplicate by key\n const combinedOptions = [...options, ...newOptions];\n if (freeSoloItem) combinedOptions.push(freeSoloItem);\n\n // Deduplicate using getItemKey\n const uniqueKeys = new Set();\n return combinedOptions.filter((option) => {\n const key = getItemKey(option);\n if (uniqueKeys.has(key)) return false;\n uniqueKeys.add(key);\n return true;\n });\n }, [options, newOptions, freeSolo, freeSoloItem, getItemKey]);\n\n // console.log({ allOptions });\n\n return (\n <AutocompleteElementDisplay\n name={name}\n control={control}\n options={allOptions}\n {...props}\n autocompleteProps={{\n /**\n * Determines if two options should be considered equal\n * Uses the getItemKey function to compare option values\n */\n isOptionEqualToValue: (o, v) => getItemKey(o) === getItemKey(v),\n\n /**\n * Filters options based on the input value\n * Checks if the option key contains the input value (case-insensitive)\n */\n filterOptions: (options, { inputValue }) =>\n options.filter((option) =>\n getItemKey(option).toLowerCase().includes(inputValue.toLowerCase()),\n ),\n freeSolo, // Allowed to enter own string value\n autoComplete: true,\n autoHighlight: true, // The first option is highlighted by default\n openOnFocus: true, // Opens the menu when tabbed into\n\n /**\n * Custom rendering for each option in the dropdown list\n * Displays a checkbox for multiple selection if showCheckbox is true\n * Uses getItemLabel to render the option label\n */\n renderOption: (liProps, option, { selected }) => (\n <li {...liProps} key={`${name}-option-${getItemKey(option)}`}>\n {props?.showCheckbox && <Checkbox sx={{ marginRight: 1 }} checked={selected} />}\n {typeof option === \"string\" ? option : getItemLabel(option)}\n </li>\n ),\n\n /**\n * Handles changes to the selected value(s)\n * In free-solo mode, adds the new item to newOptions when selected\n * Delegates to the original onChange handler if provided\n */\n onChange: (event, value, reason, details) => {\n if (freeSolo && freeSoloItem) {\n if (stringToNewItem == undefined) {\n throw new Error(\"Must implement stringToNewItem with freeSolo!\");\n }\n setNewOptions((prev) => [...prev, freeSoloItem]);\n setFreeSoloValue(\"\");\n }\n\n autocompleteProps?.onChange?.(event, value, reason, details);\n },\n\n /**\n * Handles changes to the input text\n * Updates freeSoloValue state with the current input\n * Delegates to the original onInputChange handler if provided\n */\n onInputChange: (event, value, reason) => {\n // event.preventDefault();\n setFreeSoloValue(value);\n\n // Call the original onInputChange if it exists\n autocompleteProps?.onInputChange?.(event, value, reason);\n },\n\n /**\n * Custom rendering for the selected value(s)\n * For multiple selection, renders a Chip for each selected value\n * For single selection, renders the value as text\n * Uses getItemLabel to render the value labels\n */\n renderValue: (value, getItemProps, ownerState) => {\n const typedValue = getAutocompleteRenderValue(value, ownerState);\n\n if (Array.isArray(typedValue)) {\n return typedValue.map((v, index) => {\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n const { key, ...chipProps } = getItemProps({ index });\n\n const label = typeof v === \"string\" ? v : getItemLabel(v);\n\n // Get additional chip props based on the value if the function is provided\n const valueSpecificProps =\n typeof v !== \"string\" && getChipProps ? getChipProps({ value: v, index }) : {};\n return (\n <Chip\n key={`${name}-chip-${key}`}\n label={label}\n {...valueSpecificProps}\n {...chipProps}\n />\n );\n });\n }\n\n // @ts-expect-error a key is returned, and the linter doesn't pick this up\n const { key, ...rawChipProps } = getItemProps({ index: 0 });\n const itemProps = omit(rawChipProps, \"onDelete\");\n return (\n <Typography\n component={\"span\"}\n key={`${name}-value-${key}`}\n color={\"text.primary\"}\n {...(props?.viewOnly ? omit(itemProps, \"disabled\") : itemProps)}\n >\n {(typeof typedValue === \"string\") ? typedValue : getItemLabel(typedValue as NonNullable<TValue>)}\n </Typography>\n );\n },\n ...autocompleteProps,\n }}\n />\n );\n};\n","import type { JSX } from \"react\";\r\nimport { FormControl, FormHelperText } from \"@mui/material\";\r\nimport type { FormControlProps } from \"@mui/material\";\r\nimport { useFormContext, type RegisterOptions, type FieldValues, type Path } from \"react-hook-form\";\r\nimport { useFormError } from \"../hooks\";\r\n\r\n/**\r\n * Props for the RHFHiddenInput component\r\n */\r\nexport interface ValidationElementProps<TFieldValues extends FieldValues> {\r\n /**\r\n * Name of the field in the form\r\n */\r\n name: Path<TFieldValues>;\r\n /**\r\n * Optional validation rules\r\n */\r\n rules: RegisterOptions<TFieldValues>;\r\n /**\r\n * Props to pass to the FormControl component\r\n */\r\n formControlProps?: Omit<FormControlProps, \"error\">;\r\n}\r\n\r\nexport const ValidationElement = <TFieldValues extends FieldValues = FieldValues>({\r\n name,\r\n rules,\r\n formControlProps = {},\r\n}: ValidationElementProps<TFieldValues>): JSX.Element => {\r\n const {\r\n register,\r\n formState: { errors },\r\n } = useFormContext<TFieldValues>();\r\n\r\n const { error, helperText } = useFormError(errors, name);\r\n\r\n return (\r\n <FormControl error={error} {...formControlProps}>\r\n <input type=\"hidden\" {...register(name, rules)} />\r\n {error && <FormHelperText>{helperText}</FormHelperText>}\r\n </FormControl>\r\n );\r\n};\r\n"],"names":["merge","lodash","AutocompleteElementDisplay","viewOnly","disableUnderline","textFieldProps","autocompleteProps","props","autocompleteAdjustedProps","useMemo","textFieldAdjustedProps","jsx","AutocompleteElement","getAutocompleteRenderValue","value","ownerState","get","useFormError","errors","name","fieldError","useOnMount","callback","hasMounted","useRef","useEffect","omit","ObjectElementDisplay","options","getItemKey","getItemLabel","getChipProps","stringToNewItem","freeSolo","control","field","useController","freeSoloValue","setFreeSoloValue","useState","newOptions","setNewOptions","newFieldOptions","option","freeSoloItem","item","itemKey","allOptions","combinedOptions","uniqueKeys","key","o","v","inputValue","liProps","selected","createElement","Checkbox","event","reason","details","prev","getItemProps","typedValue","index","chipProps","label","valueSpecificProps","Chip","rawChipProps","itemProps","Typography","ValidationElement","rules","formControlProps","register","useFormContext","error","helperText","jsxs","FormControl","FormHelperText"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chris-c-brine/rhf-mui-kit",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "React Hook Form components with Material UI integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",