FPSMS-frontend
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

165 行
4.8 KiB

  1. "use client";
  2. import {
  3. Autocomplete,
  4. MenuItem,
  5. TextField,
  6. Checkbox,
  7. Chip,
  8. } from "@mui/material";
  9. import {
  10. Controller,
  11. FieldValues,
  12. Path,
  13. Control,
  14. RegisterOptions,
  15. } from "react-hook-form";
  16. import { useTranslation } from "react-i18next";
  17. import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
  18. import CheckBoxIcon from "@mui/icons-material/CheckBox";
  19. const icon = <CheckBoxOutlineBlankIcon fontSize="medium" />;
  20. const checkedIcon = <CheckBoxIcon fontSize="medium" />;
  21. // label -> e.g. code - name -> 001 - WL
  22. // name -> WL
  23. interface Props<
  24. T extends { id?: number | string | null; label?: string; name?: string },
  25. TField extends FieldValues,
  26. > {
  27. control: Control<TField>;
  28. options: T[];
  29. name: Path<TField>; // register name
  30. label?: string; // display label
  31. noOptionsText?: string;
  32. isMultiple?: boolean;
  33. rules?: RegisterOptions<FieldValues>;
  34. disabled?: boolean;
  35. }
  36. function ControlledAutoComplete<
  37. T extends { id?: number | string; label?: string; name?: string },
  38. TField extends FieldValues,
  39. >(props: Props<T, TField>) {
  40. const { t } = useTranslation();
  41. const {
  42. control,
  43. options,
  44. name,
  45. label,
  46. noOptionsText,
  47. isMultiple,
  48. rules,
  49. disabled,
  50. } = props;
  51. // set default value if value is null
  52. if (!Boolean(isMultiple) && !Boolean(control._formValues[name])) {
  53. control._formValues[name] = options[0]?.id ?? undefined;
  54. } else if (Boolean(isMultiple) && !Boolean(control._formValues[name])) {
  55. control._formValues[name] = [];
  56. }
  57. return (
  58. <Controller
  59. name={name}
  60. control={control}
  61. rules={rules}
  62. render={({ field, fieldState, formState }) => {
  63. return isMultiple ? (
  64. <Autocomplete
  65. multiple
  66. disableClearable
  67. disableCloseOnSelect
  68. // disablePortal
  69. disabled={disabled}
  70. noOptionsText={noOptionsText ?? t("No Options")}
  71. value={options.filter((option) => {
  72. return field.value?.includes(option.id);
  73. })}
  74. options={options}
  75. getOptionLabel={(option) => option.label ?? option.name!}
  76. isOptionEqualToValue={(option, value) => option.id === value.id}
  77. renderOption={(params, option, { selected }) => {
  78. return (
  79. <li {...params} key={option?.id}>
  80. <Checkbox
  81. icon={icon}
  82. checkedIcon={checkedIcon}
  83. checked={selected}
  84. style={{ marginRight: 8 }}
  85. />
  86. {option.label ?? option.name}
  87. </li>
  88. );
  89. }}
  90. renderTags={(tagValue, getTagProps) => {
  91. return tagValue.map((option, index) => (
  92. <Chip
  93. {...getTagProps({ index })}
  94. key={option?.id}
  95. label={option.label ?? option.name}
  96. />
  97. ));
  98. }}
  99. onChange={(event, value) => {
  100. field.onChange(value?.map((v) => v.id));
  101. }}
  102. onBlur={field.onBlur}
  103. renderInput={(params) => (
  104. <TextField
  105. {...params}
  106. error={Boolean(formState.errors[name])}
  107. variant="outlined"
  108. label={label}
  109. />
  110. )}
  111. />
  112. ) : (
  113. <Autocomplete
  114. disableClearable
  115. // disablePortal
  116. disabled={disabled}
  117. noOptionsText={noOptionsText ?? t("No Options")}
  118. value={
  119. options.find((option) => option.id === field.value) ?? options[0]
  120. }
  121. options={options}
  122. getOptionLabel={(option) => option.label ?? option.name!}
  123. isOptionEqualToValue={(option, value) => option?.id === value?.id}
  124. renderOption={(params, option) => {
  125. return (
  126. <MenuItem {...params} key={option?.id} value={option.id}>
  127. {option.label ?? option.name}
  128. </MenuItem>
  129. );
  130. }}
  131. renderTags={(tagValue, getTagProps) => {
  132. return tagValue.map((option, index) => (
  133. <Chip
  134. {...getTagProps({ index })}
  135. key={option?.id}
  136. label={option.label ?? option.name}
  137. />
  138. ));
  139. }}
  140. onChange={(event, value) => {
  141. field.onChange(value?.id ?? null);
  142. }}
  143. onBlur={field.onBlur}
  144. renderInput={(params) => (
  145. <TextField
  146. {...params}
  147. error={Boolean(formState.errors[name])}
  148. variant="outlined"
  149. label={label}
  150. />
  151. )}
  152. />
  153. );
  154. }}
  155. />
  156. );
  157. }
  158. export default ControlledAutoComplete;