mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 02:44:55 +00:00
Merge pull request #14130 from strapi/custom-fields/cm-input
[Custom fields] Add custom field input component in CM edit view
This commit is contained in:
commit
752b671487
@ -133,7 +133,10 @@
|
||||
},
|
||||
"custom_field": {
|
||||
"type": "customField",
|
||||
"customField": "plugin::mycustomfields.color"
|
||||
"customField": "plugin::mycustomfields.color",
|
||||
"options": {
|
||||
"format": "hex"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Stack } from '@strapi/design-system/Stack';
|
||||
import { Field, FieldHint, FieldError, FieldLabel } from '@strapi/design-system/Field';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
const ColorPickerInput = () => {
|
||||
return <div>TODO: Color Picker Input Component</div>;
|
||||
const ColorPickerInput = ({
|
||||
attribute,
|
||||
description,
|
||||
error,
|
||||
hint,
|
||||
id,
|
||||
intlLabel,
|
||||
name,
|
||||
onChange,
|
||||
required,
|
||||
value,
|
||||
}) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<Stack spacing={1}>
|
||||
<Field
|
||||
name={name}
|
||||
id={name}
|
||||
error={error && formatMessage(error)}
|
||||
hint={description && formatMessage(description)}
|
||||
>
|
||||
<FieldLabel required={required}>{formatMessage(intlLabel)}</FieldLabel>
|
||||
<input type="color" id={name} name={name} value={value || ''} onChange={onChange} />
|
||||
<FieldHint />
|
||||
<FieldError />
|
||||
</Field>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorPickerInput;
|
||||
|
||||
@ -5,7 +5,7 @@ import get from 'lodash/get';
|
||||
import omit from 'lodash/omit';
|
||||
import take from 'lodash/take';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { GenericInput, NotAllowedInput, useLibrary } from '@strapi/helper-plugin';
|
||||
import { GenericInput, NotAllowedInput, useLibrary, useCustomFields } from '@strapi/helper-plugin';
|
||||
import { useContentTypeLayout } from '../../hooks';
|
||||
import { getFieldName } from '../../utils';
|
||||
import Wysiwyg from '../Wysiwyg';
|
||||
@ -39,9 +39,10 @@ function Inputs({
|
||||
const { fields } = useLibrary();
|
||||
const { formatMessage } = useIntl();
|
||||
const { contentType: currentContentTypeLayout } = useContentTypeLayout();
|
||||
const customFieldsRegistry = useCustomFields();
|
||||
|
||||
const disabled = useMemo(() => !get(metadatas, 'editable', true), [metadatas]);
|
||||
const type = fieldSchema.type;
|
||||
const { type, customField: customFieldUid } = fieldSchema;
|
||||
const error = get(formErrors, [keys], null);
|
||||
|
||||
const fieldName = useMemo(() => {
|
||||
@ -164,6 +165,19 @@ function Inputs({
|
||||
|
||||
const { label, description, placeholder, visible } = metadatas;
|
||||
|
||||
// Memoize the component to avoid remounting it and losing state
|
||||
const CustomFieldInput = useMemo(() => {
|
||||
if (customFieldUid) {
|
||||
const customField = customFieldsRegistry.get(customFieldUid);
|
||||
const CustomFieldInput = React.lazy(customField.components.Input);
|
||||
|
||||
return CustomFieldInput;
|
||||
}
|
||||
|
||||
// Not a custom field, component won't be used
|
||||
return null;
|
||||
}, [customFieldUid, customFieldsRegistry]);
|
||||
|
||||
if (visible === false) {
|
||||
return null;
|
||||
}
|
||||
@ -217,6 +231,18 @@ function Inputs({
|
||||
);
|
||||
}
|
||||
|
||||
const customInputs = {
|
||||
json: InputJSON,
|
||||
uid: InputUID,
|
||||
media: fields.media,
|
||||
wysiwyg: Wysiwyg,
|
||||
...fields,
|
||||
};
|
||||
|
||||
if (customFieldUid) {
|
||||
customInputs[customFieldUid] = CustomFieldInput;
|
||||
}
|
||||
|
||||
return (
|
||||
<GenericInput
|
||||
attribute={fieldSchema}
|
||||
@ -229,13 +255,7 @@ function Inputs({
|
||||
error={error}
|
||||
labelAction={labelAction}
|
||||
contentTypeUID={currentContentTypeLayout.uid}
|
||||
customInputs={{
|
||||
json: InputJSON,
|
||||
uid: InputUID,
|
||||
media: fields.media,
|
||||
wysiwyg: Wysiwyg,
|
||||
...fields,
|
||||
}}
|
||||
customInputs={customInputs}
|
||||
multiple={fieldSchema.multiple || false}
|
||||
name={keys}
|
||||
onChange={onChange}
|
||||
@ -243,7 +263,7 @@ function Inputs({
|
||||
placeholder={placeholder ? { id: placeholder, defaultMessage: placeholder } : null}
|
||||
required={fieldSchema.required || false}
|
||||
step={step}
|
||||
type={inputType}
|
||||
type={customFieldUid || inputType}
|
||||
// validations={validations}
|
||||
value={inputValue}
|
||||
withDefaultValue={false}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import React, { Suspense, memo, useCallback, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import get from 'lodash/get';
|
||||
import { CheckPermissions, useTracking, LinkButton } from '@strapi/helper-plugin';
|
||||
import {
|
||||
CheckPermissions,
|
||||
LoadingIndicatorPage,
|
||||
useTracking,
|
||||
LinkButton,
|
||||
} from '@strapi/helper-plugin';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { ContentLayout } from '@strapi/design-system/Layout';
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
@ -131,99 +136,101 @@ const EditView = ({
|
||||
<ContentLayout>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={9} s={12}>
|
||||
<Stack spacing={6}>
|
||||
{formattedContentTypeLayout.map((row, index) => {
|
||||
if (isDynamicZone(row)) {
|
||||
const {
|
||||
0: {
|
||||
0: { name, fieldSchema, metadatas, labelAction },
|
||||
},
|
||||
} = row;
|
||||
<Suspense fallback={<LoadingIndicatorPage />}>
|
||||
<Stack spacing={6}>
|
||||
{formattedContentTypeLayout.map((row, index) => {
|
||||
if (isDynamicZone(row)) {
|
||||
const {
|
||||
0: {
|
||||
0: { name, fieldSchema, metadatas, labelAction },
|
||||
},
|
||||
} = row;
|
||||
|
||||
return (
|
||||
<Box key={index}>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={12} s={12} xs={12}>
|
||||
<DynamicZone
|
||||
name={name}
|
||||
fieldSchema={fieldSchema}
|
||||
labelAction={labelAction}
|
||||
metadatas={metadatas}
|
||||
/>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box key={index}>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={12} s={12} xs={12}>
|
||||
<DynamicZone
|
||||
name={name}
|
||||
fieldSchema={fieldSchema}
|
||||
labelAction={labelAction}
|
||||
metadatas={metadatas}
|
||||
/>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
<Box
|
||||
key={index}
|
||||
hasRadius
|
||||
background="neutral0"
|
||||
shadow="tableShadow"
|
||||
paddingLeft={6}
|
||||
paddingRight={6}
|
||||
paddingTop={6}
|
||||
paddingBottom={6}
|
||||
borderColor="neutral150"
|
||||
>
|
||||
<Stack spacing={6}>
|
||||
{row.map((grid, gridIndex) => {
|
||||
return (
|
||||
<Grid gap={4} key={gridIndex}>
|
||||
{grid.map(
|
||||
({ fieldSchema, labelAction, metadatas, name, size }) => {
|
||||
const isComponent = fieldSchema.type === 'component';
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={index}
|
||||
hasRadius
|
||||
background="neutral0"
|
||||
shadow="tableShadow"
|
||||
paddingLeft={6}
|
||||
paddingRight={6}
|
||||
paddingTop={6}
|
||||
paddingBottom={6}
|
||||
borderColor="neutral150"
|
||||
>
|
||||
<Stack spacing={6}>
|
||||
{row.map((grid, gridIndex) => {
|
||||
return (
|
||||
<Grid gap={4} key={gridIndex}>
|
||||
{grid.map(
|
||||
({ fieldSchema, labelAction, metadatas, name, size }) => {
|
||||
const isComponent = fieldSchema.type === 'component';
|
||||
if (isComponent) {
|
||||
const {
|
||||
component,
|
||||
max,
|
||||
min,
|
||||
repeatable = false,
|
||||
required = false,
|
||||
} = fieldSchema;
|
||||
|
||||
if (isComponent) {
|
||||
const {
|
||||
component,
|
||||
max,
|
||||
min,
|
||||
repeatable = false,
|
||||
required = false,
|
||||
} = fieldSchema;
|
||||
return (
|
||||
<GridItem col={size} s={12} xs={12} key={component}>
|
||||
<FieldComponent
|
||||
componentUid={component}
|
||||
labelAction={labelAction}
|
||||
isRepeatable={repeatable}
|
||||
intlLabel={{
|
||||
id: metadatas.label,
|
||||
defaultMessage: metadatas.label,
|
||||
}}
|
||||
max={max}
|
||||
min={min}
|
||||
name={name}
|
||||
required={required}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GridItem col={size} s={12} xs={12} key={component}>
|
||||
<FieldComponent
|
||||
componentUid={component}
|
||||
<GridItem col={size} key={name} s={12} xs={12}>
|
||||
<Inputs
|
||||
fieldSchema={fieldSchema}
|
||||
keys={name}
|
||||
labelAction={labelAction}
|
||||
isRepeatable={repeatable}
|
||||
intlLabel={{
|
||||
id: metadatas.label,
|
||||
defaultMessage: metadatas.label,
|
||||
}}
|
||||
max={max}
|
||||
min={min}
|
||||
name={name}
|
||||
required={required}
|
||||
metadatas={metadatas}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GridItem col={size} key={name} s={12} xs={12}>
|
||||
<Inputs
|
||||
fieldSchema={fieldSchema}
|
||||
keys={name}
|
||||
labelAction={labelAction}
|
||||
metadatas={metadatas}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Suspense>
|
||||
</GridItem>
|
||||
<GridItem col={3} s={12}>
|
||||
<Stack spacing={2}>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user