|
@@ -26,7 +26,11 @@ import {
|
|
VECTOR_FIELDS_OPTIONS,
|
|
VECTOR_FIELDS_OPTIONS,
|
|
ANALYZER_OPTIONS,
|
|
ANALYZER_OPTIONS,
|
|
} from './Constants';
|
|
} from './Constants';
|
|
-import { CreateFieldsProps, CreateFieldType, FieldType } from './Types';
|
|
|
|
|
|
+import {
|
|
|
|
+ CreateFieldsProps,
|
|
|
|
+ CreateFieldType,
|
|
|
|
+ FieldType,
|
|
|
|
+} from '../../databases/collections/Types';
|
|
import { DataTypeEnum, VectorTypes } from '@/consts';
|
|
import { DataTypeEnum, VectorTypes } from '@/consts';
|
|
import {
|
|
import {
|
|
DEFAULT_ATTU_DIM,
|
|
DEFAULT_ATTU_DIM,
|
|
@@ -39,17 +43,23 @@ import CustomIconButton from '@/components/customButton/CustomIconButton';
|
|
import EditAnalyzerDialog from '@/pages/dialogs/EditAnalyzerDialog';
|
|
import EditAnalyzerDialog from '@/pages/dialogs/EditAnalyzerDialog';
|
|
|
|
|
|
const useStyles = makeStyles((theme: Theme) => ({
|
|
const useStyles = makeStyles((theme: Theme) => ({
|
|
- optionalWrapper: {
|
|
|
|
|
|
+ scalarFieldsWrapper: {
|
|
width: '100%',
|
|
width: '100%',
|
|
paddingRight: theme.spacing(1),
|
|
paddingRight: theme.spacing(1),
|
|
overflowY: 'auto',
|
|
overflowY: 'auto',
|
|
},
|
|
},
|
|
|
|
+ title: {
|
|
|
|
+ '& button': {
|
|
|
|
+ position: 'relative',
|
|
|
|
+ top: '-1px',
|
|
|
|
+ marginLeft: 4,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
rowWrapper: {
|
|
rowWrapper: {
|
|
display: 'flex',
|
|
display: 'flex',
|
|
flexWrap: 'nowrap',
|
|
flexWrap: 'nowrap',
|
|
alignItems: 'center',
|
|
alignItems: 'center',
|
|
gap: '8px',
|
|
gap: '8px',
|
|
- flex: '1 0 auto',
|
|
|
|
marginBottom: 4,
|
|
marginBottom: 4,
|
|
'& .MuiFormLabel-root': {
|
|
'& .MuiFormLabel-root': {
|
|
fontSize: 14,
|
|
fontSize: 14,
|
|
@@ -74,6 +84,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|
width: '150px',
|
|
width: '150px',
|
|
marginTop: '-20px',
|
|
marginTop: '-20px',
|
|
},
|
|
},
|
|
|
|
+ smallSelect: {
|
|
|
|
+ width: '105px',
|
|
|
|
+ marginTop: '-20px',
|
|
|
|
+ },
|
|
autoIdSelect: {
|
|
autoIdSelect: {
|
|
width: '120px',
|
|
width: '120px',
|
|
marginTop: '-20px',
|
|
marginTop: '-20px',
|
|
@@ -162,7 +176,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
const AddIcon = icons.addOutline;
|
|
const AddIcon = icons.addOutline;
|
|
const RemoveIcon = icons.remove;
|
|
const RemoveIcon = icons.remove;
|
|
|
|
|
|
- const { requiredFields, optionalFields } = useMemo(
|
|
|
|
|
|
+ const { requiredFields, scalarFields } = useMemo(
|
|
() =>
|
|
() =>
|
|
fields.reduce(
|
|
fields.reduce(
|
|
(acc, field) => {
|
|
(acc, field) => {
|
|
@@ -170,10 +184,11 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
const requiredTypes: CreateFieldType[] = [
|
|
const requiredTypes: CreateFieldType[] = [
|
|
'primaryKey',
|
|
'primaryKey',
|
|
'defaultVector',
|
|
'defaultVector',
|
|
|
|
+ 'vector',
|
|
];
|
|
];
|
|
const key = requiredTypes.includes(createType)
|
|
const key = requiredTypes.includes(createType)
|
|
? 'requiredFields'
|
|
? 'requiredFields'
|
|
- : 'optionalFields';
|
|
|
|
|
|
+ : 'scalarFields';
|
|
|
|
|
|
acc[key].push({
|
|
acc[key].push({
|
|
...field,
|
|
...field,
|
|
@@ -184,7 +199,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
},
|
|
},
|
|
{
|
|
{
|
|
requiredFields: [] as FieldType[],
|
|
requiredFields: [] as FieldType[],
|
|
- optionalFields: [] as FieldType[],
|
|
|
|
|
|
+ scalarFields: [] as FieldType[],
|
|
}
|
|
}
|
|
),
|
|
),
|
|
|
|
|
|
@@ -192,17 +207,18 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
);
|
|
);
|
|
|
|
|
|
const getSelector = (
|
|
const getSelector = (
|
|
- type: 'all' | 'vector' | 'element' | 'primaryKey',
|
|
|
|
|
|
+ type: 'scalar' | 'vector' | 'element' | 'primaryKey',
|
|
label: string,
|
|
label: string,
|
|
value: number,
|
|
value: number,
|
|
- onChange: (value: DataTypeEnum) => void
|
|
|
|
|
|
+ onChange: (value: DataTypeEnum) => void,
|
|
|
|
+ className: string = classes.select
|
|
) => {
|
|
) => {
|
|
let _options = ALL_OPTIONS;
|
|
let _options = ALL_OPTIONS;
|
|
switch (type) {
|
|
switch (type) {
|
|
case 'primaryKey':
|
|
case 'primaryKey':
|
|
_options = PRIMARY_FIELDS_OPTIONS;
|
|
_options = PRIMARY_FIELDS_OPTIONS;
|
|
break;
|
|
break;
|
|
- case 'all':
|
|
|
|
|
|
+ case 'scalar':
|
|
_options = ALL_OPTIONS;
|
|
_options = ALL_OPTIONS;
|
|
break;
|
|
break;
|
|
case 'vector':
|
|
case 'vector':
|
|
@@ -213,6 +229,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
d =>
|
|
d =>
|
|
d.label !== 'Array' &&
|
|
d.label !== 'Array' &&
|
|
d.label !== 'JSON' &&
|
|
d.label !== 'JSON' &&
|
|
|
|
+ d.label !== 'VarChar(BM25)' &&
|
|
!d.label.includes('Vector')
|
|
!d.label.includes('Vector')
|
|
);
|
|
);
|
|
break;
|
|
break;
|
|
@@ -222,7 +239,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
|
|
|
|
return (
|
|
return (
|
|
<CustomSelector
|
|
<CustomSelector
|
|
- wrapperClass={classes.select}
|
|
|
|
|
|
+ wrapperClass={className}
|
|
options={_options}
|
|
options={_options}
|
|
size="small"
|
|
size="small"
|
|
onChange={e => {
|
|
onChange={e => {
|
|
@@ -557,13 +574,17 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
return (
|
|
return (
|
|
<div className={classes.analyzerInput}>
|
|
<div className={classes.analyzerInput}>
|
|
<Checkbox
|
|
<Checkbox
|
|
- checked={!!field.enable_analyzer}
|
|
|
|
|
|
+ checked={
|
|
|
|
+ !!field.enable_analyzer ||
|
|
|
|
+ field.data_type === DataTypeEnum.VarCharBM25
|
|
|
|
+ }
|
|
size="small"
|
|
size="small"
|
|
onChange={() => {
|
|
onChange={() => {
|
|
changeFields(field.id!, {
|
|
changeFields(field.id!, {
|
|
enable_analyzer: !field.enable_analyzer,
|
|
enable_analyzer: !field.enable_analyzer,
|
|
});
|
|
});
|
|
}}
|
|
}}
|
|
|
|
+ disabled={field.data_type === DataTypeEnum.VarCharBM25}
|
|
/>
|
|
/>
|
|
<CustomSelector
|
|
<CustomSelector
|
|
wrapperClass="select"
|
|
wrapperClass="select"
|
|
@@ -572,13 +593,19 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
onChange={e => {
|
|
onChange={e => {
|
|
changeFields(field.id!, { analyzer_params: e.target.value });
|
|
changeFields(field.id!, { analyzer_params: e.target.value });
|
|
}}
|
|
}}
|
|
- disabled={!field.enable_analyzer}
|
|
|
|
|
|
+ disabled={
|
|
|
|
+ !field.enable_analyzer &&
|
|
|
|
+ field.data_type !== DataTypeEnum.VarCharBM25
|
|
|
|
+ }
|
|
value={analyzer}
|
|
value={analyzer}
|
|
variant="filled"
|
|
variant="filled"
|
|
label={collectionTrans('analyzer')}
|
|
label={collectionTrans('analyzer')}
|
|
/>
|
|
/>
|
|
<CustomIconButton
|
|
<CustomIconButton
|
|
- disabled={!field.enable_analyzer}
|
|
|
|
|
|
+ disabled={
|
|
|
|
+ !field.enable_analyzer &&
|
|
|
|
+ field.data_type !== DataTypeEnum.VarCharBM25
|
|
|
|
+ }
|
|
onClick={() => {
|
|
onClick={() => {
|
|
setDialog2({
|
|
setDialog2({
|
|
open: true,
|
|
open: true,
|
|
@@ -625,6 +652,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
// remove varchar params, if not varchar
|
|
// remove varchar params, if not varchar
|
|
if (
|
|
if (
|
|
updatedField.data_type !== DataTypeEnum.VarChar &&
|
|
updatedField.data_type !== DataTypeEnum.VarChar &&
|
|
|
|
+ updatedField.data_type !== DataTypeEnum.VarCharBM25 &&
|
|
updatedField.element_type !== DataTypeEnum.VarChar
|
|
updatedField.element_type !== DataTypeEnum.VarChar
|
|
) {
|
|
) {
|
|
delete updatedField.max_length;
|
|
delete updatedField.max_length;
|
|
@@ -647,15 +675,17 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
setFields(newFields);
|
|
setFields(newFields);
|
|
};
|
|
};
|
|
|
|
|
|
- const handleAddNewField = (index: number) => {
|
|
|
|
|
|
+ const handleAddNewField = (index: number, type = DataTypeEnum.Int16) => {
|
|
const id = generateId();
|
|
const id = generateId();
|
|
const newDefaultItem: FieldType = {
|
|
const newDefaultItem: FieldType = {
|
|
name: '',
|
|
name: '',
|
|
- data_type: DataTypeEnum.Int16,
|
|
|
|
|
|
+ data_type: type,
|
|
is_primary_key: false,
|
|
is_primary_key: false,
|
|
description: '',
|
|
description: '',
|
|
isDefault: false,
|
|
isDefault: false,
|
|
dim: DEFAULT_ATTU_DIM,
|
|
dim: DEFAULT_ATTU_DIM,
|
|
|
|
+ max_length: DEFAULT_ATTU_VARCHAR_MAX_LENGTH,
|
|
|
|
+ enable_analyzer: type === DataTypeEnum.VarCharBM25,
|
|
id,
|
|
id,
|
|
};
|
|
};
|
|
const newValidation = {
|
|
const newValidation = {
|
|
@@ -740,7 +770,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
{generateDimension(field)}
|
|
{generateDimension(field)}
|
|
{generateDesc(field)}
|
|
{generateDesc(field)}
|
|
<IconButton
|
|
<IconButton
|
|
- onClick={() => handleAddNewField(index)}
|
|
|
|
|
|
+ onClick={() => handleAddNewField(index, field.data_type)}
|
|
classes={{ root: classes.iconBtn }}
|
|
classes={{ root: classes.iconBtn }}
|
|
aria-label="add"
|
|
aria-label="add"
|
|
size="large"
|
|
size="large"
|
|
@@ -751,7 +781,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
);
|
|
);
|
|
};
|
|
};
|
|
|
|
|
|
- const generateNonRequiredRow = (
|
|
|
|
|
|
+ const generateScalarFieldRow = (
|
|
field: FieldType,
|
|
field: FieldType,
|
|
index: number,
|
|
index: number,
|
|
fields: FieldType[]
|
|
fields: FieldType[]
|
|
@@ -776,10 +806,12 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
<div className={`${classes.rowWrapper}`}>
|
|
<div className={`${classes.rowWrapper}`}>
|
|
{generateFieldName(field)}
|
|
{generateFieldName(field)}
|
|
{getSelector(
|
|
{getSelector(
|
|
- 'all',
|
|
|
|
|
|
+ 'scalar',
|
|
collectionTrans('fieldType'),
|
|
collectionTrans('fieldType'),
|
|
field.data_type,
|
|
field.data_type,
|
|
- (value: DataTypeEnum) => changeFields(field.id!, { data_type: value })
|
|
|
|
|
|
+ (value: DataTypeEnum) =>
|
|
|
|
+ changeFields(field.id!, { data_type: value }),
|
|
|
|
+ classes.smallSelect
|
|
)}
|
|
)}
|
|
|
|
|
|
{isArray
|
|
{isArray
|
|
@@ -788,7 +820,8 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
collectionTrans('elementType'),
|
|
collectionTrans('elementType'),
|
|
field.element_type || DEFAULT_ATTU_ELEMENT_TYPE,
|
|
field.element_type || DEFAULT_ATTU_ELEMENT_TYPE,
|
|
(value: DataTypeEnum) =>
|
|
(value: DataTypeEnum) =>
|
|
- changeFields(field.id!, { element_type: value })
|
|
|
|
|
|
+ changeFields(field.id!, { element_type: value }),
|
|
|
|
+ classes.smallSelect
|
|
)
|
|
)
|
|
: null}
|
|
: null}
|
|
|
|
|
|
@@ -814,7 +847,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
|
|
|
|
<IconButton
|
|
<IconButton
|
|
onClick={() => {
|
|
onClick={() => {
|
|
- handleAddNewField(index);
|
|
|
|
|
|
+ handleAddNewField(index, field.data_type);
|
|
}}
|
|
}}
|
|
classes={{ root: classes.iconBtn }}
|
|
classes={{ root: classes.iconBtn }}
|
|
aria-label="add"
|
|
aria-label="add"
|
|
@@ -837,23 +870,36 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
);
|
|
);
|
|
};
|
|
};
|
|
|
|
|
|
- const generateVectorRow = (field: FieldType, index: number) => {
|
|
|
|
|
|
+ const generateFunctionRow = (
|
|
|
|
+ field: FieldType,
|
|
|
|
+ index: number,
|
|
|
|
+ fields: FieldType[],
|
|
|
|
+ requiredFields: FieldType[]
|
|
|
|
+ ) => {
|
|
return (
|
|
return (
|
|
<div className={`${classes.rowWrapper}`}>
|
|
<div className={`${classes.rowWrapper}`}>
|
|
{generateFieldName(field)}
|
|
{generateFieldName(field)}
|
|
{getSelector(
|
|
{getSelector(
|
|
- 'all',
|
|
|
|
|
|
+ 'vector',
|
|
collectionTrans('fieldType'),
|
|
collectionTrans('fieldType'),
|
|
field.data_type,
|
|
field.data_type,
|
|
(value: DataTypeEnum) => changeFields(field.id!, { data_type: value })
|
|
(value: DataTypeEnum) => changeFields(field.id!, { data_type: value })
|
|
)}
|
|
)}
|
|
|
|
|
|
- {generateDimension(field)}
|
|
|
|
|
|
+ {generateMaxLength(field)}
|
|
|
|
+ {generateDefaultValue(field)}
|
|
{generateDesc(field)}
|
|
{generateDesc(field)}
|
|
|
|
|
|
|
|
+ <div className={classes.paramsGrp}>
|
|
|
|
+ {generateAnalyzerCheckBox(field, fields)}
|
|
|
|
+ {generateTextMatchCheckBox(field, fields)}
|
|
|
|
+ {generatePartitionKeyCheckbox(field, fields)}
|
|
|
|
+ {generateNullableCheckbox(field, fields)}
|
|
|
|
+ </div>
|
|
|
|
+
|
|
<IconButton
|
|
<IconButton
|
|
onClick={() => {
|
|
onClick={() => {
|
|
- handleAddNewField(index);
|
|
|
|
|
|
+ handleAddNewField(index, field.data_type);
|
|
}}
|
|
}}
|
|
classes={{ root: classes.iconBtn }}
|
|
classes={{ root: classes.iconBtn }}
|
|
aria-label="add"
|
|
aria-label="add"
|
|
@@ -861,17 +907,59 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
>
|
|
>
|
|
<AddIcon />
|
|
<AddIcon />
|
|
</IconButton>
|
|
</IconButton>
|
|
|
|
+
|
|
|
|
+ {requiredFields.length !== 2 && (
|
|
|
|
+ <IconButton
|
|
|
|
+ onClick={() => {
|
|
|
|
+ const id = field.id || '';
|
|
|
|
+ handleRemoveField(id);
|
|
|
|
+ }}
|
|
|
|
+ classes={{ root: classes.iconBtn }}
|
|
|
|
+ aria-label="delete"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <RemoveIcon />
|
|
|
|
+ </IconButton>
|
|
|
|
+ )}
|
|
|
|
+ </div>
|
|
|
|
+ );
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const generateVectorRow = (field: FieldType, index: number) => {
|
|
|
|
+ return (
|
|
|
|
+ <div className={`${classes.rowWrapper}`}>
|
|
|
|
+ {generateFieldName(field)}
|
|
|
|
+ {getSelector(
|
|
|
|
+ 'vector',
|
|
|
|
+ `${collectionTrans('vectorType')} `,
|
|
|
|
+ field.data_type,
|
|
|
|
+ (value: DataTypeEnum) => changeFields(field.id!, { data_type: value })
|
|
|
|
+ )}
|
|
|
|
+
|
|
|
|
+ {generateDimension(field)}
|
|
|
|
+ {generateDesc(field)}
|
|
|
|
+
|
|
<IconButton
|
|
<IconButton
|
|
- onClick={() => {
|
|
|
|
- const id = field.id || '';
|
|
|
|
- handleRemoveField(id);
|
|
|
|
- }}
|
|
|
|
|
|
+ onClick={() => handleAddNewField(index, field.data_type)}
|
|
classes={{ root: classes.iconBtn }}
|
|
classes={{ root: classes.iconBtn }}
|
|
- aria-label="delete"
|
|
|
|
|
|
+ aria-label="add"
|
|
size="large"
|
|
size="large"
|
|
>
|
|
>
|
|
- <RemoveIcon />
|
|
|
|
|
|
+ <AddIcon />
|
|
</IconButton>
|
|
</IconButton>
|
|
|
|
+ {requiredFields.length !== 2 && (
|
|
|
|
+ <IconButton
|
|
|
|
+ onClick={() => {
|
|
|
|
+ const id = field.id || '';
|
|
|
|
+ handleRemoveField(id);
|
|
|
|
+ }}
|
|
|
|
+ classes={{ root: classes.iconBtn }}
|
|
|
|
+ aria-label="delete"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <RemoveIcon />
|
|
|
|
+ </IconButton>
|
|
|
|
+ )}
|
|
</div>
|
|
</div>
|
|
);
|
|
);
|
|
};
|
|
};
|
|
@@ -879,44 +967,63 @@ const CreateFields: FC<CreateFieldsProps> = ({
|
|
const generateRequiredFieldRow = (
|
|
const generateRequiredFieldRow = (
|
|
field: FieldType,
|
|
field: FieldType,
|
|
autoID: boolean,
|
|
autoID: boolean,
|
|
- index: number
|
|
|
|
|
|
+ index: number,
|
|
|
|
+ fields: FieldType[],
|
|
|
|
+ requiredFields: FieldType[]
|
|
) => {
|
|
) => {
|
|
// required type is primaryKey or defaultVector
|
|
// required type is primaryKey or defaultVector
|
|
if (field.createType === 'primaryKey') {
|
|
if (field.createType === 'primaryKey') {
|
|
return generatePrimaryKeyRow(field, autoID);
|
|
return generatePrimaryKeyRow(field, autoID);
|
|
}
|
|
}
|
|
- // use defaultVector as default return type
|
|
|
|
- return generateDefaultVectorRow(field, index);
|
|
|
|
- };
|
|
|
|
|
|
|
|
- const generateOptionalFieldRow = (
|
|
|
|
- field: FieldType,
|
|
|
|
- index: number,
|
|
|
|
- fields: FieldType[]
|
|
|
|
- ) => {
|
|
|
|
- // optional type is vector or number
|
|
|
|
- if (field.createType === 'vector') {
|
|
|
|
- return generateVectorRow(field, index);
|
|
|
|
|
|
+ if (field.data_type === DataTypeEnum.VarCharBM25) {
|
|
|
|
+ return generateFunctionRow(field, index, fields, requiredFields);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (field.createType === 'defaultVector') {
|
|
|
|
+ return generateDefaultVectorRow(field, index);
|
|
}
|
|
}
|
|
|
|
|
|
- // use number as default createType
|
|
|
|
- return generateNonRequiredRow(field, index, fields);
|
|
|
|
|
|
+ // generate other vector rows
|
|
|
|
+ return generateVectorRow(field, index);
|
|
};
|
|
};
|
|
|
|
|
|
return (
|
|
return (
|
|
<>
|
|
<>
|
|
|
|
+ <h4 className={classes.title}>
|
|
|
|
+ {`${collectionTrans('idAndVectorFields')}(${requiredFields.length})`}
|
|
|
|
+ </h4>
|
|
{requiredFields.map((field, index) => (
|
|
{requiredFields.map((field, index) => (
|
|
<Fragment key={field.id}>
|
|
<Fragment key={field.id}>
|
|
- {generateRequiredFieldRow(field, autoID, index)}
|
|
|
|
|
|
+ {generateRequiredFieldRow(
|
|
|
|
+ field,
|
|
|
|
+ autoID,
|
|
|
|
+ index,
|
|
|
|
+ fields,
|
|
|
|
+ requiredFields
|
|
|
|
+ )}
|
|
</Fragment>
|
|
</Fragment>
|
|
))}
|
|
))}
|
|
- <div className={classes.optionalWrapper}>
|
|
|
|
- {optionalFields.map((field, index) => (
|
|
|
|
|
|
+ <h4 className={classes.title}>
|
|
|
|
+ {`${collectionTrans('scalarFields')}(${scalarFields.length})`}
|
|
|
|
+ <IconButton
|
|
|
|
+ onClick={() => {
|
|
|
|
+ handleAddNewField(requiredFields.length + 1);
|
|
|
|
+ }}
|
|
|
|
+ classes={{ root: classes.iconBtn }}
|
|
|
|
+ aria-label="add"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <AddIcon />
|
|
|
|
+ </IconButton>
|
|
|
|
+ </h4>
|
|
|
|
+ <div className={classes.scalarFieldsWrapper}>
|
|
|
|
+ {scalarFields.map((field, index) => (
|
|
<Fragment key={field.id}>
|
|
<Fragment key={field.id}>
|
|
- {generateOptionalFieldRow(
|
|
|
|
|
|
+ {generateScalarFieldRow(
|
|
field,
|
|
field,
|
|
index + requiredFields.length,
|
|
index + requiredFields.length,
|
|
- optionalFields
|
|
|
|
|
|
+ fields
|
|
)}
|
|
)}
|
|
</Fragment>
|
|
</Fragment>
|
|
))}
|
|
))}
|