new react-openapi
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { TextField, Autocomplete } from "@mui/material";
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { TextField, Autocomplete, Chip, Box } from "@mui/material";
|
||||
import DoneIcon from "@mui/icons-material/Done";
|
||||
import type { FieldConfig } from "../../../types";
|
||||
|
||||
interface Props {
|
||||
@@ -12,20 +13,86 @@ interface Props {
|
||||
}
|
||||
|
||||
export function FkMultiSelectField({ field, value, onChange, fkOptions, fkLoading, onOpen }: Props) {
|
||||
console.log(`[FkMultiSelectField] render field="${field.name}" fkOptions=${fkOptions ? `${fkOptions.length} items` : "undefined"} fkLoading=${fkLoading} value=${JSON.stringify(value)}`);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [frozenValue, setFrozenValue] = useState<any[]>([]);
|
||||
|
||||
const handleOpen = () => {
|
||||
onOpen?.();
|
||||
setFrozenValue(value ?? []);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const sortedOptions = useMemo(() => {
|
||||
const sel = new Set(frozenValue);
|
||||
const picked: { value: any; label: string }[] = [];
|
||||
const rest: { value: any; label: string }[] = [];
|
||||
for (const opt of fkOptions ?? []) {
|
||||
(sel.has(opt.value) ? picked : rest).push(opt);
|
||||
}
|
||||
return [...picked, ...rest];
|
||||
}, [fkOptions, frozenValue]);
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
multiple
|
||||
options={fkOptions ?? []}
|
||||
disableCloseOnSelect
|
||||
open={open}
|
||||
onOpen={handleOpen}
|
||||
onClose={handleClose}
|
||||
options={sortedOptions}
|
||||
getOptionLabel={(o) => o.label}
|
||||
value={fkOptions?.filter((o) => (value ?? []).includes(o.value)) ?? []}
|
||||
onChange={(_, newVal) => onChange(newVal.map((v) => v.value))}
|
||||
onOpen={() => onOpen?.()}
|
||||
loading={fkLoading}
|
||||
renderOption={(props, option, { selected }) => (
|
||||
<li {...props}>
|
||||
{selected ? (
|
||||
<DoneIcon sx={{ fontSize: 14, mr: 1, color: "primary.main" }} />
|
||||
) : (
|
||||
<Box sx={{ width: 22, mr: 1 }} />
|
||||
)}
|
||||
{option.label}
|
||||
</li>
|
||||
)}
|
||||
renderTags={(tagValue, getTagProps) => {
|
||||
const maxChips = 1;
|
||||
return (
|
||||
<>
|
||||
{tagValue.slice(0, maxChips).map((tag, index) => {
|
||||
const { key, ...tagProps } = getTagProps({ index });
|
||||
return (
|
||||
<Chip
|
||||
key={key}
|
||||
{...tagProps}
|
||||
label={tag.label.length > 10 ? `${tag.label.slice(0, 8)}..` : tag.label}
|
||||
size="small"
|
||||
onClick={open ? handleClose : handleOpen}
|
||||
sx={{ cursor: "pointer" }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{tagValue.length > maxChips && (
|
||||
<Chip
|
||||
label={`+${tagValue.length - maxChips}`}
|
||||
size="small"
|
||||
onClick={open ? handleClose : handleOpen}
|
||||
sx={{ cursor: "pointer" }}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={field.label} helperText={field.description} size="small" />
|
||||
<TextField {...params} label={field.label} helperText={field.description || undefined} size="small" />
|
||||
)}
|
||||
size="small"
|
||||
sx={{
|
||||
"& .MuiAutocomplete-popupIndicator, & .MuiAutocomplete-clearIndicator": { width: 20, height: 20, fontSize: 16 },
|
||||
}}
|
||||
disabled={field.readOnly}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user