@strapi/admin 4.3.6 → 4.4.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/StrapiApp.js +4 -12
- package/admin/src/components/Providers/index.js +14 -10
- package/admin/src/content-manager/components/FieldTypeIcon/index.js +31 -1
- package/admin/src/content-manager/components/Inputs/index.js +30 -10
- package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +7 -2
- package/admin/src/content-manager/pages/EditSettingsView/index.js +2 -1
- package/admin/src/content-manager/pages/EditView/index.js +91 -84
- package/admin/src/core/apis/CustomFields.js +80 -0
- package/admin/src/core/apis/index.js +1 -0
- package/build/524.2437fb56.chunk.js +644 -0
- package/build/{Admin-authenticatedApp.350ee81d.chunk.js → Admin-authenticatedApp.aaa66872.chunk.js} +3 -3
- package/build/{admin-app.05edc328.chunk.js → admin-app.1f9e13f8.chunk.js} +16 -16
- package/build/content-manager.86f7594d.chunk.js +1178 -0
- package/build/content-type-builder-list-view.9b874fd4.chunk.js +194 -0
- package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +1 -0
- package/build/content-type-builder.47ab07ad.chunk.js +145 -0
- package/build/index.html +1 -1
- package/build/{main.f220d4d1.js → main.7db3414f.js} +1181 -1181
- package/build/{runtime~main.14842096.js → runtime~main.c1c5510b.js} +2 -2
- package/package.json +6 -6
- package/build/611.aa894d27.chunk.js +0 -158
- package/build/content-manager.62390771.chunk.js +0 -1178
- package/build/content-type-builder-list-view.76a69473.chunk.js +0 -194
- package/build/content-type-builder-translation-en-json.201bfb78.chunk.js +0 -1
- package/build/content-type-builder.42cecba9.chunk.js +0 -142
package/admin/src/StrapiApp.js
CHANGED
|
@@ -8,7 +8,7 @@ import invariant from 'invariant';
|
|
|
8
8
|
import { Helmet } from 'react-helmet';
|
|
9
9
|
import { basename, createHook } from './core/utils';
|
|
10
10
|
import configureStore from './core/store/configureStore';
|
|
11
|
-
import { Plugin } from './core/apis';
|
|
11
|
+
import { customFields, Plugin } from './core/apis';
|
|
12
12
|
import App from './pages/App';
|
|
13
13
|
import AuthLogo from './assets/images/logo_strapi_auth_v4.png';
|
|
14
14
|
import MenuLogo from './assets/images/logo_strapi_menu.png';
|
|
@@ -47,6 +47,7 @@ class StrapiApp {
|
|
|
47
47
|
this.admin = {
|
|
48
48
|
injectionZones,
|
|
49
49
|
};
|
|
50
|
+
this.customFields = customFields;
|
|
50
51
|
|
|
51
52
|
this.menu = [];
|
|
52
53
|
this.settings = {
|
|
@@ -280,17 +281,7 @@ class StrapiApp {
|
|
|
280
281
|
|
|
281
282
|
async initialize() {
|
|
282
283
|
Object.keys(this.appPlugins).forEach((plugin) => {
|
|
283
|
-
this.appPlugins[plugin].register(
|
|
284
|
-
addComponents: this.addComponents,
|
|
285
|
-
addCorePluginMenuLink: this.addCorePluginMenuLink,
|
|
286
|
-
addFields: this.addFields,
|
|
287
|
-
addMenuLink: this.addMenuLink,
|
|
288
|
-
addMiddlewares: this.addMiddlewares,
|
|
289
|
-
addReducers: this.addReducers,
|
|
290
|
-
createHook: this.createHook,
|
|
291
|
-
createSettingSection: this.createSettingSection,
|
|
292
|
-
registerPlugin: this.registerPlugin,
|
|
293
|
-
});
|
|
284
|
+
this.appPlugins[plugin].register(this);
|
|
294
285
|
});
|
|
295
286
|
}
|
|
296
287
|
|
|
@@ -430,6 +421,7 @@ class StrapiApp {
|
|
|
430
421
|
authLogo={this.configurations.authLogo}
|
|
431
422
|
components={components}
|
|
432
423
|
fields={fields}
|
|
424
|
+
customFields={this.customFields}
|
|
433
425
|
localeNames={localeNames}
|
|
434
426
|
getAdminInjectedComponents={this.getAdminInjectedComponents}
|
|
435
427
|
getPlugin={this.getPlugin}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { QueryClientProvider, QueryClient } from 'react-query';
|
|
4
|
-
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
|
4
|
+
import { LibraryProvider, CustomFieldsProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
|
5
5
|
import { Provider } from 'react-redux';
|
|
6
6
|
import { AdminContext } from '../../contexts';
|
|
7
7
|
import ConfigurationsProvider from '../ConfigurationsProvider';
|
|
@@ -25,6 +25,7 @@ const Providers = ({
|
|
|
25
25
|
authLogo,
|
|
26
26
|
children,
|
|
27
27
|
components,
|
|
28
|
+
customFields,
|
|
28
29
|
fields,
|
|
29
30
|
getAdminInjectedComponents,
|
|
30
31
|
getPlugin,
|
|
@@ -64,15 +65,17 @@ const Providers = ({
|
|
|
64
65
|
settings={settings}
|
|
65
66
|
>
|
|
66
67
|
<LibraryProvider components={components} fields={fields}>
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
<CustomFieldsProvider customFields={customFields}>
|
|
69
|
+
<LanguageProvider messages={messages} localeNames={localeNames}>
|
|
70
|
+
<AutoReloadOverlayBlockerProvider>
|
|
71
|
+
<OverlayBlocker>
|
|
72
|
+
<GuidedTour>
|
|
73
|
+
<Notifications>{children}</Notifications>
|
|
74
|
+
</GuidedTour>
|
|
75
|
+
</OverlayBlocker>
|
|
76
|
+
</AutoReloadOverlayBlockerProvider>
|
|
77
|
+
</LanguageProvider>
|
|
78
|
+
</CustomFieldsProvider>
|
|
76
79
|
</LibraryProvider>
|
|
77
80
|
</StrapiAppProvider>
|
|
78
81
|
</ConfigurationsProvider>
|
|
@@ -88,6 +91,7 @@ Providers.propTypes = {
|
|
|
88
91
|
authLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired,
|
|
89
92
|
children: PropTypes.element.isRequired,
|
|
90
93
|
components: PropTypes.object.isRequired,
|
|
94
|
+
customFields: PropTypes.object.isRequired,
|
|
91
95
|
fields: PropTypes.object.isRequired,
|
|
92
96
|
getAdminInjectedComponents: PropTypes.func.isRequired,
|
|
93
97
|
getPlugin: PropTypes.func.isRequired,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import { Box } from '@strapi/design-system/Box';
|
|
4
|
+
import { useCustomFields } from '@strapi/helper-plugin';
|
|
3
5
|
import Date from '@strapi/icons/Date';
|
|
4
6
|
import Boolean from '@strapi/icons/Boolean';
|
|
5
7
|
import Email from '@strapi/icons/Email';
|
|
@@ -40,10 +42,38 @@ const iconByTypes = {
|
|
|
40
42
|
dynamiczone: <DynamicZone />,
|
|
41
43
|
};
|
|
42
44
|
|
|
43
|
-
const FieldTypeIcon = ({ type }) =>
|
|
45
|
+
const FieldTypeIcon = ({ type, customFieldUid }) => {
|
|
46
|
+
const customFieldsRegistry = useCustomFields();
|
|
47
|
+
|
|
48
|
+
let Compo = iconByTypes[type];
|
|
49
|
+
|
|
50
|
+
if (customFieldUid) {
|
|
51
|
+
const customField = customFieldsRegistry.get(customFieldUid);
|
|
52
|
+
const CustomFieldIcon = customField.icon;
|
|
53
|
+
|
|
54
|
+
if (CustomFieldIcon) {
|
|
55
|
+
Compo = (
|
|
56
|
+
<Box marginRight={3} width={7} height={6}>
|
|
57
|
+
<CustomFieldIcon />
|
|
58
|
+
</Box>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!iconByTypes[type]) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return Compo;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
FieldTypeIcon.defaultProps = {
|
|
71
|
+
customFieldUid: null,
|
|
72
|
+
};
|
|
44
73
|
|
|
45
74
|
FieldTypeIcon.propTypes = {
|
|
46
75
|
type: PropTypes.string.isRequired,
|
|
76
|
+
customFieldUid: PropTypes.string,
|
|
47
77
|
};
|
|
48
78
|
|
|
49
79
|
export default FieldTypeIcon;
|
|
@@ -5,7 +5,7 @@ import get from 'lodash/get';
|
|
|
5
5
|
import omit from 'lodash/omit';
|
|
6
6
|
import take from 'lodash/take';
|
|
7
7
|
import isEqual from 'react-fast-compare';
|
|
8
|
-
import { GenericInput, NotAllowedInput, useLibrary } from '@strapi/helper-plugin';
|
|
8
|
+
import { GenericInput, NotAllowedInput, useLibrary, useCustomFields } from '@strapi/helper-plugin';
|
|
9
9
|
import { useContentTypeLayout } from '../../hooks';
|
|
10
10
|
import { getFieldName } from '../../utils';
|
|
11
11
|
import Wysiwyg from '../Wysiwyg';
|
|
@@ -39,9 +39,10 @@ function Inputs({
|
|
|
39
39
|
const { fields } = useLibrary();
|
|
40
40
|
const { formatMessage } = useIntl();
|
|
41
41
|
const { contentType: currentContentTypeLayout } = useContentTypeLayout();
|
|
42
|
+
const customFieldsRegistry = useCustomFields();
|
|
42
43
|
|
|
43
44
|
const disabled = useMemo(() => !get(metadatas, 'editable', true), [metadatas]);
|
|
44
|
-
const type = fieldSchema
|
|
45
|
+
const { type, customField: customFieldUid } = fieldSchema;
|
|
45
46
|
const error = get(formErrors, [keys], null);
|
|
46
47
|
|
|
47
48
|
const fieldName = useMemo(() => {
|
|
@@ -191,6 +192,19 @@ function Inputs({
|
|
|
191
192
|
return minutes % metadatas.step === 0 ? metadatas.step : step;
|
|
192
193
|
}, [inputType, inputValue, metadatas.step, step]);
|
|
193
194
|
|
|
195
|
+
// Memoize the component to avoid remounting it and losing state
|
|
196
|
+
const CustomFieldInput = useMemo(() => {
|
|
197
|
+
if (customFieldUid) {
|
|
198
|
+
const customField = customFieldsRegistry.get(customFieldUid);
|
|
199
|
+
const CustomFieldInput = React.lazy(customField.components.Input);
|
|
200
|
+
|
|
201
|
+
return CustomFieldInput;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Not a custom field, component won't be used
|
|
205
|
+
return null;
|
|
206
|
+
}, [customFieldUid, customFieldsRegistry]);
|
|
207
|
+
|
|
194
208
|
if (visible === false) {
|
|
195
209
|
return null;
|
|
196
210
|
}
|
|
@@ -244,6 +258,18 @@ function Inputs({
|
|
|
244
258
|
);
|
|
245
259
|
}
|
|
246
260
|
|
|
261
|
+
const customInputs = {
|
|
262
|
+
json: InputJSON,
|
|
263
|
+
uid: InputUID,
|
|
264
|
+
media: fields.media,
|
|
265
|
+
wysiwyg: Wysiwyg,
|
|
266
|
+
...fields,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
if (customFieldUid) {
|
|
270
|
+
customInputs[customFieldUid] = CustomFieldInput;
|
|
271
|
+
}
|
|
272
|
+
|
|
247
273
|
return (
|
|
248
274
|
<GenericInput
|
|
249
275
|
attribute={fieldSchema}
|
|
@@ -256,13 +282,7 @@ function Inputs({
|
|
|
256
282
|
error={error}
|
|
257
283
|
labelAction={labelAction}
|
|
258
284
|
contentTypeUID={currentContentTypeLayout.uid}
|
|
259
|
-
customInputs={
|
|
260
|
-
json: InputJSON,
|
|
261
|
-
uid: InputUID,
|
|
262
|
-
media: fields.media,
|
|
263
|
-
wysiwyg: Wysiwyg,
|
|
264
|
-
...fields,
|
|
265
|
-
}}
|
|
285
|
+
customInputs={customInputs}
|
|
266
286
|
multiple={fieldSchema.multiple || false}
|
|
267
287
|
name={keys}
|
|
268
288
|
onChange={onChange}
|
|
@@ -270,7 +290,7 @@ function Inputs({
|
|
|
270
290
|
placeholder={placeholder ? { id: placeholder, defaultMessage: placeholder } : null}
|
|
271
291
|
required={fieldSchema.required || false}
|
|
272
292
|
step={inputStep}
|
|
273
|
-
type={inputType}
|
|
293
|
+
type={customFieldUid || inputType}
|
|
274
294
|
// validations={validations}
|
|
275
295
|
value={inputValue}
|
|
276
296
|
withDefaultValue={false}
|
|
@@ -26,7 +26,7 @@ const HeaderContainer = styled(Flex)`
|
|
|
26
26
|
}
|
|
27
27
|
`;
|
|
28
28
|
|
|
29
|
-
const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) => {
|
|
29
|
+
const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type, customFieldUid }) => {
|
|
30
30
|
const { selectedField } = useLayoutDnd();
|
|
31
31
|
const { formatMessage } = useIntl();
|
|
32
32
|
|
|
@@ -47,7 +47,7 @@ const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) =>
|
|
|
47
47
|
<form onSubmit={onSubmit}>
|
|
48
48
|
<ModalHeader>
|
|
49
49
|
<HeaderContainer>
|
|
50
|
-
<FieldTypeIcon type={getAttrType(
|
|
50
|
+
<FieldTypeIcon type={getAttrType()} customFieldUid={customFieldUid} />
|
|
51
51
|
<Typography fontWeight="bold" textColor="neutral800" as="h2" id="title">
|
|
52
52
|
{formatMessage(
|
|
53
53
|
{
|
|
@@ -81,7 +81,12 @@ const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) =>
|
|
|
81
81
|
);
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
FormModal.defaultProps = {
|
|
85
|
+
customFieldUid: null,
|
|
86
|
+
};
|
|
87
|
+
|
|
84
88
|
FormModal.propTypes = {
|
|
89
|
+
customFieldUid: PropTypes.string,
|
|
85
90
|
onSubmit: PropTypes.func.isRequired,
|
|
86
91
|
onToggle: PropTypes.func.isRequired,
|
|
87
92
|
onMetaChange: PropTypes.func.isRequired,
|
|
@@ -371,9 +371,10 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
|
|
|
371
371
|
<ModalForm
|
|
372
372
|
onSubmit={handleMetaSubmit}
|
|
373
373
|
onToggle={handleToggleModal}
|
|
374
|
-
type={get(attributes, [metaToEdit, 'type'], '')}
|
|
375
374
|
onMetaChange={handleMetaChange}
|
|
376
375
|
onSizeChange={handleSizeChange}
|
|
376
|
+
type={get(attributes, [metaToEdit, 'type'], '')}
|
|
377
|
+
customFieldUid={get(attributes, [metaToEdit, 'customField'], '')}
|
|
377
378
|
/>
|
|
378
379
|
)}
|
|
379
380
|
</Main>
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import React, { memo, useCallback, useMemo } from 'react';
|
|
1
|
+
import React, { Suspense, memo, useCallback, useMemo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CheckPermissions,
|
|
6
|
+
LoadingIndicatorPage,
|
|
7
|
+
useTracking,
|
|
8
|
+
LinkButton,
|
|
9
|
+
} from '@strapi/helper-plugin';
|
|
5
10
|
import { useIntl } from 'react-intl';
|
|
6
11
|
import { ContentLayout } from '@strapi/design-system/Layout';
|
|
7
12
|
import { Box } from '@strapi/design-system/Box';
|
|
@@ -131,99 +136,101 @@ const EditView = ({
|
|
|
131
136
|
<ContentLayout>
|
|
132
137
|
<Grid gap={4}>
|
|
133
138
|
<GridItem col={9} s={12}>
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
0: {
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
<Suspense fallback={<LoadingIndicatorPage />}>
|
|
140
|
+
<Stack spacing={6}>
|
|
141
|
+
{formattedContentTypeLayout.map((row, index) => {
|
|
142
|
+
if (isDynamicZone(row)) {
|
|
143
|
+
const {
|
|
144
|
+
0: {
|
|
145
|
+
0: { name, fieldSchema, metadatas, labelAction },
|
|
146
|
+
},
|
|
147
|
+
} = row;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<Box key={index}>
|
|
151
|
+
<Grid gap={4}>
|
|
152
|
+
<GridItem col={12} s={12} xs={12}>
|
|
153
|
+
<DynamicZone
|
|
154
|
+
name={name}
|
|
155
|
+
fieldSchema={fieldSchema}
|
|
156
|
+
labelAction={labelAction}
|
|
157
|
+
metadatas={metadatas}
|
|
158
|
+
/>
|
|
159
|
+
</GridItem>
|
|
160
|
+
</Grid>
|
|
161
|
+
</Box>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
142
164
|
|
|
143
165
|
return (
|
|
144
|
-
<Box
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
<Box
|
|
167
|
+
key={index}
|
|
168
|
+
hasRadius
|
|
169
|
+
background="neutral0"
|
|
170
|
+
shadow="tableShadow"
|
|
171
|
+
paddingLeft={6}
|
|
172
|
+
paddingRight={6}
|
|
173
|
+
paddingTop={6}
|
|
174
|
+
paddingBottom={6}
|
|
175
|
+
borderColor="neutral150"
|
|
176
|
+
>
|
|
177
|
+
<Stack spacing={6}>
|
|
178
|
+
{row.map((grid, gridIndex) => {
|
|
179
|
+
return (
|
|
180
|
+
<Grid gap={4} key={gridIndex}>
|
|
181
|
+
{grid.map(
|
|
182
|
+
({ fieldSchema, labelAction, metadatas, name, size }) => {
|
|
183
|
+
const isComponent = fieldSchema.type === 'component';
|
|
158
184
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
paddingTop={6}
|
|
168
|
-
paddingBottom={6}
|
|
169
|
-
borderColor="neutral150"
|
|
170
|
-
>
|
|
171
|
-
<Stack spacing={6}>
|
|
172
|
-
{row.map((grid, gridIndex) => {
|
|
173
|
-
return (
|
|
174
|
-
<Grid gap={4} key={gridIndex}>
|
|
175
|
-
{grid.map(
|
|
176
|
-
({ fieldSchema, labelAction, metadatas, name, size }) => {
|
|
177
|
-
const isComponent = fieldSchema.type === 'component';
|
|
185
|
+
if (isComponent) {
|
|
186
|
+
const {
|
|
187
|
+
component,
|
|
188
|
+
max,
|
|
189
|
+
min,
|
|
190
|
+
repeatable = false,
|
|
191
|
+
required = false,
|
|
192
|
+
} = fieldSchema;
|
|
178
193
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
return (
|
|
195
|
+
<GridItem col={size} s={12} xs={12} key={component}>
|
|
196
|
+
<FieldComponent
|
|
197
|
+
componentUid={component}
|
|
198
|
+
labelAction={labelAction}
|
|
199
|
+
isRepeatable={repeatable}
|
|
200
|
+
intlLabel={{
|
|
201
|
+
id: metadatas.label,
|
|
202
|
+
defaultMessage: metadatas.label,
|
|
203
|
+
}}
|
|
204
|
+
max={max}
|
|
205
|
+
min={min}
|
|
206
|
+
name={name}
|
|
207
|
+
required={required}
|
|
208
|
+
/>
|
|
209
|
+
</GridItem>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
187
212
|
|
|
188
213
|
return (
|
|
189
|
-
<GridItem col={size}
|
|
190
|
-
<
|
|
191
|
-
|
|
214
|
+
<GridItem col={size} key={name} s={12} xs={12}>
|
|
215
|
+
<Inputs
|
|
216
|
+
fieldSchema={fieldSchema}
|
|
217
|
+
keys={name}
|
|
192
218
|
labelAction={labelAction}
|
|
193
|
-
|
|
194
|
-
intlLabel={{
|
|
195
|
-
id: metadatas.label,
|
|
196
|
-
defaultMessage: metadatas.label,
|
|
197
|
-
}}
|
|
198
|
-
max={max}
|
|
199
|
-
min={min}
|
|
200
|
-
name={name}
|
|
201
|
-
required={required}
|
|
219
|
+
metadatas={metadatas}
|
|
202
220
|
/>
|
|
203
221
|
</GridItem>
|
|
204
222
|
);
|
|
205
223
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
)}
|
|
219
|
-
</Grid>
|
|
220
|
-
);
|
|
221
|
-
})}
|
|
222
|
-
</Stack>
|
|
223
|
-
</Box>
|
|
224
|
-
);
|
|
225
|
-
})}
|
|
226
|
-
</Stack>
|
|
224
|
+
)}
|
|
225
|
+
</Grid>
|
|
226
|
+
);
|
|
227
|
+
})}
|
|
228
|
+
</Stack>
|
|
229
|
+
</Box>
|
|
230
|
+
);
|
|
231
|
+
})}
|
|
232
|
+
</Stack>
|
|
233
|
+
</Suspense>
|
|
227
234
|
</GridItem>
|
|
228
235
|
<GridItem col={3} s={12}>
|
|
229
236
|
<Stack spacing={2}>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import invariant from 'invariant';
|
|
2
|
+
|
|
3
|
+
const ALLOWED_TYPES = [
|
|
4
|
+
'biginteger',
|
|
5
|
+
'boolean',
|
|
6
|
+
'date',
|
|
7
|
+
'datetime',
|
|
8
|
+
'decimal',
|
|
9
|
+
'email',
|
|
10
|
+
'enumeration',
|
|
11
|
+
'float',
|
|
12
|
+
'integer',
|
|
13
|
+
'json',
|
|
14
|
+
'password',
|
|
15
|
+
'richtext',
|
|
16
|
+
'string',
|
|
17
|
+
'text',
|
|
18
|
+
'time',
|
|
19
|
+
'timestamp',
|
|
20
|
+
'uid',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
class CustomFields {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.customFields = {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
register(customFields) {
|
|
29
|
+
if (Array.isArray(customFields)) {
|
|
30
|
+
// If several custom fields are passed, register them one by one
|
|
31
|
+
customFields.forEach(customField => {
|
|
32
|
+
this.register(customField);
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
// Handle individual custom field
|
|
36
|
+
const { name, pluginId, type, intlLabel, intlDescription, components } = customFields;
|
|
37
|
+
|
|
38
|
+
// Ensure required attributes are provided
|
|
39
|
+
invariant(name, 'A name must be provided');
|
|
40
|
+
invariant(type, 'A type must be provided');
|
|
41
|
+
invariant(intlLabel, 'An intlLabel must be provided');
|
|
42
|
+
invariant(intlDescription, 'An intlDescription must be provided');
|
|
43
|
+
invariant(components, 'A components object must be provided');
|
|
44
|
+
invariant(components.Input, 'An Input component must be provided');
|
|
45
|
+
|
|
46
|
+
// Ensure the type is valid
|
|
47
|
+
invariant(
|
|
48
|
+
ALLOWED_TYPES.includes(type),
|
|
49
|
+
`Custom field type: '${type}' is not a valid Strapi type or it can't be used with a Custom Field`
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Ensure name has no special characters
|
|
53
|
+
const isValidObjectKey = /^(?![0-9])[a-zA-Z0-9$_-]+$/g;
|
|
54
|
+
invariant(
|
|
55
|
+
isValidObjectKey.test(name),
|
|
56
|
+
`Custom field name: '${name}' is not a valid object key`
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// When no plugin is specified, default to the global namespace
|
|
60
|
+
const uid = pluginId ? `plugin::${pluginId}.${name}` : `global::${name}`;
|
|
61
|
+
|
|
62
|
+
// Ensure the uid is unique
|
|
63
|
+
const uidAlreadyUsed = Object.prototype.hasOwnProperty.call(this.customFields, uid);
|
|
64
|
+
invariant(!uidAlreadyUsed, `Custom field: '${uid}' has already been registered`);
|
|
65
|
+
|
|
66
|
+
this.customFields[uid] = customFields;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getAll() {
|
|
71
|
+
return this.customFields;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get(uid) {
|
|
75
|
+
return this.customFields[uid];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Export an instance since it's a singleton
|
|
80
|
+
export default new CustomFields();
|