mirror of
https://github.com/strapi/strapi.git
synced 2025-11-08 14:19:40 +00:00
Allow component creation
This commit is contained in:
parent
5d0384f045
commit
bc28d3daf1
@ -1,26 +1,32 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Creatable from 'react-select/creatable';
|
import Creatable from 'react-select/creatable';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import { SelectWrapper, SelectNav } from 'strapi-helper-plugin';
|
||||||
SelectWrapper,
|
import { ErrorMessage } from '@buffetjs/styles';
|
||||||
SelectNav,
|
|
||||||
useGlobalContext,
|
|
||||||
} from 'strapi-helper-plugin';
|
|
||||||
import useDataManager from '../../hooks/useDataManager';
|
import useDataManager from '../../hooks/useDataManager';
|
||||||
|
|
||||||
const CreatableSelect = ({ label, name }) => {
|
const CreatableSelect = ({ error, label, onChange, name }) => {
|
||||||
const { allComponentsCategories } = useDataManager();
|
const { allComponentsCategories } = useDataManager();
|
||||||
console.log({ label });
|
|
||||||
const { formatMessage } = useGlobalContext();
|
const handleChange = (inputValue, actionMeta) => {
|
||||||
const handleInputChange = (newValue, actionMeta) => {
|
const { action } = actionMeta;
|
||||||
console.log({ actionMeta, newValue });
|
|
||||||
console.log(actionMeta);
|
if (action === 'clear') {
|
||||||
|
onChange({ target: { name, value: '' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'create-option' || action === 'select-option') {
|
||||||
|
onChange({ target: { name, value: inputValue.value } });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
control: (base, state) => ({
|
control: (base, state) => ({
|
||||||
...base,
|
...base,
|
||||||
border: state.isFocused
|
border: state.isFocused
|
||||||
? '1px solid #78caff !important'
|
? '1px solid #78caff !important'
|
||||||
|
: error
|
||||||
|
? '1px solid red !important'
|
||||||
: '1px solid #E3E9F3 !important',
|
: '1px solid #E3E9F3 !important',
|
||||||
}),
|
}),
|
||||||
menu: base => {
|
menu: base => {
|
||||||
@ -36,7 +42,7 @@ const CreatableSelect = ({ label, name }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectWrapper className="form-group">
|
<SelectWrapper className="form-group" style={{ marginBottom: 0 }}>
|
||||||
<SelectNav>
|
<SelectNav>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor={name}>{label}</label>
|
<label htmlFor={name}>{label}</label>
|
||||||
@ -44,18 +50,24 @@ const CreatableSelect = ({ label, name }) => {
|
|||||||
</SelectNav>
|
</SelectNav>
|
||||||
<Creatable
|
<Creatable
|
||||||
isClearable
|
isClearable
|
||||||
onInputChange={handleInputChange}
|
onChange={handleChange}
|
||||||
styles={styles}
|
styles={styles}
|
||||||
options={formatOptions()}
|
options={formatOptions()}
|
||||||
// menuIsOpen
|
|
||||||
/>
|
/>
|
||||||
|
{error && <ErrorMessage style={{ paddingTop: 10 }}>{error}</ErrorMessage>}
|
||||||
</SelectWrapper>
|
</SelectWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CreatableSelect.defaultProps = {
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
CreatableSelect.propTypes = {
|
CreatableSelect.propTypes = {
|
||||||
|
error: PropTypes.string,
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CreatableSelect;
|
export default CreatableSelect;
|
||||||
|
|||||||
@ -93,10 +93,16 @@ const DataManagerProvider = ({ children }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSchema = (data, schemaType, uid) => {
|
const createSchema = (data, schemaType, uid, componentCategory) => {
|
||||||
|
const type =
|
||||||
|
schemaType === 'contentType'
|
||||||
|
? 'CREATE_SCHEMA'
|
||||||
|
: 'CREATE_COMPONENT_SCHEMA';
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'CREATE_SCHEMA',
|
type,
|
||||||
data,
|
data,
|
||||||
|
componentCategory,
|
||||||
schemaType,
|
schemaType,
|
||||||
uid,
|
uid,
|
||||||
});
|
});
|
||||||
@ -169,8 +175,6 @@ const DataManagerProvider = ({ children }) => {
|
|||||||
return <Redirect to={`/plugins/${pluginId}/content-types/${firstCTUid}`} />;
|
return <Redirect to={`/plugins/${pluginId}/content-types/${firstCTUid}`} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({ modifiedData });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataManagerContext.Provider
|
<DataManagerContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
@ -88,10 +88,27 @@ const reducer = (state, action) => {
|
|||||||
attributes: {},
|
attributes: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const key =
|
|
||||||
action.schemaType === 'contentType' ? 'contentTypes' : 'components';
|
|
||||||
|
|
||||||
return state.updateIn([key, action.uid], () => fromJS(newSchema));
|
// const key =
|
||||||
|
// action.schemaType === 'contentType' ? 'contentTypes' : 'components';
|
||||||
|
|
||||||
|
return state.updateIn(['contentTypes', action.uid], () =>
|
||||||
|
fromJS(newSchema)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case 'CREATE_COMPONENT_SCHEMA': {
|
||||||
|
const newSchema = {
|
||||||
|
uid: action.uid,
|
||||||
|
isTemporary: true,
|
||||||
|
category: action.componentCategory,
|
||||||
|
schema: {
|
||||||
|
...action.data,
|
||||||
|
attributes: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return state.updateIn(['components', action.uid], () =>
|
||||||
|
fromJS(newSchema)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case 'EDIT_ATTRIBUTE': {
|
case 'EDIT_ATTRIBUTE': {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import HeaderNavLink from '../../components/HeaderNavLink';
|
|||||||
import getTrad from '../../utils/getTrad';
|
import getTrad from '../../utils/getTrad';
|
||||||
import getAttributes from './utils/attributes';
|
import getAttributes from './utils/attributes';
|
||||||
import forms from './utils/forms';
|
import forms from './utils/forms';
|
||||||
import { createUid } from './utils/createUid';
|
import { createComponentUid, createUid } from './utils/createUid';
|
||||||
import init from './init';
|
import init from './init';
|
||||||
import reducer, { initialState } from './reducer';
|
import reducer, { initialState } from './reducer';
|
||||||
import RelationForm from '../../components/RelationForm';
|
import RelationForm from '../../components/RelationForm';
|
||||||
@ -57,6 +57,7 @@ const FormModal = () => {
|
|||||||
const {
|
const {
|
||||||
addAttribute,
|
addAttribute,
|
||||||
contentTypes,
|
contentTypes,
|
||||||
|
components,
|
||||||
createSchema,
|
createSchema,
|
||||||
modifiedData: allDataSchema,
|
modifiedData: allDataSchema,
|
||||||
sortedContentTypesList,
|
sortedContentTypesList,
|
||||||
@ -155,13 +156,14 @@ const FormModal = () => {
|
|||||||
const checkFormValidity = async () => {
|
const checkFormValidity = async () => {
|
||||||
let schema;
|
let schema;
|
||||||
|
|
||||||
if (state.modalType === 'contentType' || state.modalType === 'component') {
|
if (state.modalType === 'contentType') {
|
||||||
schema = forms[state.modalType].schema(Object.keys(contentTypes));
|
schema = forms.contentType.schema(Object.keys(contentTypes));
|
||||||
} else if (
|
} else if (state.modalType === 'component') {
|
||||||
state.modalType === 'attribute'
|
schema = forms.component.schema(
|
||||||
// && state.forTarget !== 'components' &&
|
Object.keys(components),
|
||||||
// state.forTarget !== 'component'
|
modifiedData.category || ''
|
||||||
) {
|
);
|
||||||
|
} else if (state.modalType === 'attribute') {
|
||||||
const type =
|
const type =
|
||||||
state.attributeType === 'relation' ? 'relation' : modifiedData.type;
|
state.attributeType === 'relation' ? 'relation' : modifiedData.type;
|
||||||
|
|
||||||
@ -275,11 +277,12 @@ const FormModal = () => {
|
|||||||
await checkFormValidity();
|
await checkFormValidity();
|
||||||
const targetUid =
|
const targetUid =
|
||||||
state.forTarget === 'components' ? state.targetUid : uid;
|
state.forTarget === 'components' ? state.targetUid : uid;
|
||||||
|
const createNextSearch = searchUid => {
|
||||||
const nextSearch = `modalType=chooseAttribute&forTarget=${
|
return `modalType=chooseAttribute&forTarget=${
|
||||||
state.forTarget
|
state.forTarget
|
||||||
}&targetUid=${targetUid}&headerDisplayName=${state.headerDisplayName ||
|
}&targetUid=${searchUid}&headerDisplayName=${state.headerDisplayName ||
|
||||||
modifiedData.name}`;
|
modifiedData.name}`;
|
||||||
|
};
|
||||||
|
|
||||||
if (state.modalType === 'contentType') {
|
if (state.modalType === 'contentType') {
|
||||||
// Create the content type schema
|
// Create the content type schema
|
||||||
@ -289,7 +292,7 @@ const FormModal = () => {
|
|||||||
: 'component-categories';
|
: 'component-categories';
|
||||||
push({
|
push({
|
||||||
pathname: `/plugins/${pluginId}/${nextSlug}/${uid}`,
|
pathname: `/plugins/${pluginId}/${nextSlug}/${uid}`,
|
||||||
search: nextSearch,
|
search: createNextSearch(targetUid),
|
||||||
});
|
});
|
||||||
} else if (state.modalType === 'attribute') {
|
} else if (state.modalType === 'attribute') {
|
||||||
addAttribute(
|
addAttribute(
|
||||||
@ -299,9 +302,22 @@ const FormModal = () => {
|
|||||||
state.actionType === 'edit',
|
state.actionType === 'edit',
|
||||||
initialData
|
initialData
|
||||||
);
|
);
|
||||||
push({ search: nextSearch });
|
push({ search: createNextSearch(targetUid) });
|
||||||
|
} else if (state.modalType === 'component') {
|
||||||
|
// Create the component schema
|
||||||
|
const componentUid = createComponentUid(
|
||||||
|
modifiedData.name,
|
||||||
|
modifiedData.category
|
||||||
|
);
|
||||||
|
const { category, ...rest } = modifiedData;
|
||||||
|
createSchema(rest, 'component', componentUid, category);
|
||||||
|
|
||||||
|
push({
|
||||||
|
search: createNextSearch(componentUid),
|
||||||
|
pathname: `/plugins/${pluginId}/component-categories/${category}/${componentUid}`,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log('Do something with component later');
|
console.log('Do somethign later');
|
||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'RESET_PROPS',
|
type: 'RESET_PROPS',
|
||||||
|
|||||||
@ -9,4 +9,9 @@ const createUid = name => {
|
|||||||
return uid;
|
return uid;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { createUid, nameToSlug };
|
// From `content-type-builder/services/Components/createComponentUid`
|
||||||
|
const createComponentUid = (name, category) => {
|
||||||
|
return `${nameToSlug(category)}.${nameToSlug(name)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { createComponentUid, createUid, nameToSlug };
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import pluginId from '../../../pluginId';
|
import pluginId from '../../../pluginId';
|
||||||
import getTrad from '../../../utils/getTrad';
|
import getTrad from '../../../utils/getTrad';
|
||||||
import { createUid, nameToSlug } from './createUid';
|
import { createComponentUid, createUid, nameToSlug } from './createUid';
|
||||||
|
|
||||||
yup.addMethod(yup.mixed, 'defined', function() {
|
yup.addMethod(yup.mixed, 'defined', function() {
|
||||||
return this.test(
|
return this.test(
|
||||||
@ -18,7 +18,8 @@ yup.addMethod(yup.mixed, 'defined', function() {
|
|||||||
yup.addMethod(yup.string, 'unique', function(
|
yup.addMethod(yup.string, 'unique', function(
|
||||||
message,
|
message,
|
||||||
alreadyTakenAttributes,
|
alreadyTakenAttributes,
|
||||||
validator
|
validator,
|
||||||
|
category = ''
|
||||||
) {
|
) {
|
||||||
return this.test('unique', message, function(string) {
|
return this.test('unique', message, function(string) {
|
||||||
if (!string) {
|
if (!string) {
|
||||||
@ -26,7 +27,7 @@ yup.addMethod(yup.string, 'unique', function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !alreadyTakenAttributes.includes(
|
return !alreadyTakenAttributes.includes(
|
||||||
typeof validator === 'function' ? validator(string) : string
|
typeof validator === 'function' ? validator(string, category) : string
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -604,8 +605,6 @@ const forms = {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({ items });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items,
|
items,
|
||||||
};
|
};
|
||||||
@ -676,14 +675,44 @@ const forms = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
schema(alreadyTakenAttributes) {
|
schema(alreadyTakenAttributes, componentCategory) {
|
||||||
return yup.object().shape({
|
return yup.object().shape({
|
||||||
name: yup.string().required(),
|
name: yup
|
||||||
|
.string()
|
||||||
|
.unique(
|
||||||
|
errorsTrads.unique,
|
||||||
|
alreadyTakenAttributes,
|
||||||
|
createComponentUid,
|
||||||
|
componentCategory
|
||||||
|
)
|
||||||
|
.required(),
|
||||||
category: yup.string().required(),
|
category: yup.string().required(),
|
||||||
|
icon: yup.string().required(),
|
||||||
|
collectionName: yup.string(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
base(data) {
|
advanced() {
|
||||||
|
return {
|
||||||
|
items: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
autoFocus: true,
|
||||||
|
label: {
|
||||||
|
id: `${pluginId}.contentType.collectionName.label`,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
id: `${pluginId}.contentType.collectionName.description`,
|
||||||
|
},
|
||||||
|
name: 'collectionName',
|
||||||
|
type: 'text',
|
||||||
|
validations: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
base() {
|
||||||
const defaultItems = [
|
const defaultItems = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -706,7 +735,7 @@ const forms = {
|
|||||||
type: 'creatableSelect',
|
type: 'creatableSelect',
|
||||||
label: {
|
label: {
|
||||||
id: getTrad(
|
id: getTrad(
|
||||||
'modalForm.components.create-component.categoryLabel'
|
'modalForm.components.create-component.category.label'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
@ -714,6 +743,15 @@ const forms = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'icon',
|
||||||
|
type: 'componentIconPicker',
|
||||||
|
label: {
|
||||||
|
id: getTrad('modalForm.components.icon.label'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -246,5 +246,6 @@
|
|||||||
"modalForm.sub-header.chooseAttribute.contentType": "Select a field for your content type",
|
"modalForm.sub-header.chooseAttribute.contentType": "Select a field for your content type",
|
||||||
"modalForm.sub-header.attribute.create": "Add new {type} field",
|
"modalForm.sub-header.attribute.create": "Add new {type} field",
|
||||||
"modalForm.sub-header.attribute.edit": "Edit {name}",
|
"modalForm.sub-header.attribute.edit": "Edit {name}",
|
||||||
"modalForm.components.create-component.categoryLabel": "Select a category or enter a name to create a new one"
|
"modalForm.components.create-component.category.label": "Select a category or enter a name to create a new one",
|
||||||
|
"modalForm.components.icon.label": "Icon"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user