@strapi/admin 4.6.0-alpha.0 → 4.6.0-beta.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/content-manager/components/ComponentInitializer/index.js +1 -7
- package/admin/src/content-manager/components/{RepeatableComponent/DragPreview.js → DragLayer/ComponentDragPreview.js} +25 -12
- package/admin/src/content-manager/components/DragLayer/RelationDragPreview.js +75 -0
- package/admin/src/content-manager/components/DragLayer/index.js +23 -7
- package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +130 -84
- package/admin/src/content-manager/components/DynamicZone/index.js +99 -24
- package/admin/src/content-manager/components/DynamicZone/utils/select.js +9 -5
- package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +76 -14
- package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +23 -23
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +24 -5
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +8 -1
- package/admin/src/content-manager/components/Inputs/index.js +5 -19
- package/admin/src/content-manager/components/NonRepeatableComponent/index.js +4 -0
- package/admin/src/content-manager/components/RelationInput/RelationInput.js +203 -63
- package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +134 -21
- package/admin/src/content-manager/components/RelationInput/components/RelationList.js +1 -2
- package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
- package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +132 -10
- package/admin/src/content-manager/components/RepeatableComponent/components/Accordion.js +77 -0
- package/admin/src/content-manager/components/RepeatableComponent/components/Component.js +262 -0
- package/admin/src/content-manager/components/RepeatableComponent/{DraggedItem → components}/Preview.js +0 -0
- package/admin/src/content-manager/components/RepeatableComponent/index.js +147 -87
- package/admin/src/content-manager/components/RepeatableComponent/utils/getComponentErrorKeys.js +1 -1
- package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +1 -1
- package/admin/src/content-manager/components/Wysiwyg/Editor.js +1 -1
- package/admin/src/content-manager/hooks/index.js +2 -0
- package/admin/src/content-manager/hooks/useDragAndDrop.js +120 -0
- package/admin/src/content-manager/hooks/useKeyboardDragAndDrop.js +98 -0
- package/admin/src/content-manager/hooks/useLazyComponents/index.js +69 -0
- package/admin/src/content-manager/pages/CollectionTypeRecursivePath/components/ErrorFallback.js +13 -0
- package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +2 -1
- package/admin/src/content-manager/pages/EditView/GridRow/index.js +62 -0
- package/admin/src/content-manager/pages/EditView/index.js +74 -154
- package/admin/src/content-manager/pages/EditView/selectors.js +14 -0
- package/admin/src/content-manager/pages/EditView/utils/createAttributesLayout.js +11 -6
- package/admin/src/content-manager/pages/EditView/utils/getCustomFieldUidsFromLayout.js +18 -0
- package/admin/src/content-manager/pages/EditView/utils/index.js +1 -0
- package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +1 -1
- package/admin/src/content-manager/sharedReducers/crudReducer/actions.js +5 -0
- package/admin/src/content-manager/sharedReducers/crudReducer/constants.js +2 -0
- package/admin/src/content-manager/sharedReducers/crudReducer/reducer.js +7 -0
- package/admin/src/content-manager/utils/ItemTypes.js +1 -1
- package/admin/src/content-manager/utils/composeRefs.js +28 -0
- package/admin/src/content-manager/utils/getMaxTempKey.js +1 -1
- package/admin/src/content-manager/utils/index.js +7 -0
- package/admin/src/core/utils/axiosInstance.js +4 -2
- package/admin/src/hooks/index.js +1 -0
- package/admin/src/hooks/useFetchClient/index.js +23 -0
- package/admin/src/pages/HomePage/SocialLinks.js +4 -4
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +23 -18
- package/admin/src/translations/en.json +6 -0
- package/admin/src/translations/ru.json +789 -489
- package/admin/src/utils/fetchClient.js +45 -0
- package/admin/src/utils/getFetchClient.js +10 -0
- package/build/4306.df40a798.chunk.js +98 -0
- package/build/{4318.daf31770.chunk.js → 4318.80bdf035.chunk.js} +2 -2
- package/build/5057.195a59ff.chunk.js +65 -0
- package/build/{805.a1894307.chunk.js → 805.e991a370.chunk.js} +6 -6
- package/build/{4986.3820d11d.chunk.js → 8176.b19bc128.chunk.js} +32 -32
- package/build/{1233.32d6888d.chunk.js → 8186.55910742.chunk.js} +94 -94
- package/build/{8633.8da5488a.chunk.js → 8633.59223842.chunk.js} +1 -1
- package/build/8881.c693411a.chunk.js +245 -0
- package/build/9161.4a0ab137.chunk.js +2119 -0
- package/build/9279.6290c87a.chunk.js +117 -0
- package/build/9707.a0cc4ad8.chunk.js +70 -0
- package/build/Admin-authenticatedApp.f9e74dc0.chunk.js +80 -0
- package/build/{Admin_homePage.b4db4df8.chunk.js → Admin_homePage.8945f71a.chunk.js} +5 -5
- package/build/{Admin_marketplace.fa51405b.chunk.js → Admin_marketplace.ed754a4a.chunk.js} +1 -1
- package/build/{Admin_pluginsPage.14d2840f.chunk.js → Admin_pluginsPage.3c872de7.chunk.js} +1 -1
- package/build/{Admin_profilePage.6c2c8398.chunk.js → Admin_profilePage.c07bdf08.chunk.js} +1 -1
- package/build/{Admin_settingsPage.5e740514.chunk.js → Admin_settingsPage.50a8765b.chunk.js} +5 -5
- package/build/admin-app.2861b6d2.chunk.js +112 -0
- package/build/{admin-edit-roles-page.c7c338b3.chunk.js → admin-edit-roles-page.f407538c.chunk.js} +1 -1
- package/build/{admin-edit-users.d254c128.chunk.js → admin-edit-users.85231e4c.chunk.js} +1 -1
- package/build/{admin-users.7c423e41.chunk.js → admin-users.a2707644.chunk.js} +2 -2
- package/build/api-tokens-create-page.dd4ddfcb.chunk.js +1 -0
- package/build/api-tokens-edit-page.821c5a6c.chunk.js +1 -0
- package/build/{api-tokens-list-page.fe994b6b.chunk.js → api-tokens-list-page.700e575f.chunk.js} +1 -1
- package/build/content-manager.ee948f75.chunk.js +1186 -0
- package/build/content-type-builder-list-view.4412efc3.chunk.js +201 -0
- package/build/content-type-builder.b132b5f4.chunk.js +145 -0
- package/build/email-settings-page.db0d98d1.chunk.js +15 -0
- package/build/{en-json.7dd57947.chunk.js → en-json.4a56dca7.chunk.js} +1 -1
- package/build/index.html +1 -1
- package/build/main.faac89ee.js +2025 -0
- package/build/ru-json.8830286f.chunk.js +1 -0
- package/build/{runtime~main.6e7d95b9.js → runtime~main.75a15b8e.js} +1 -1
- package/build/{sso-settings-page.eb30a02e.chunk.js → sso-settings-page.adb12ac3.chunk.js} +1 -1
- package/build/upload-settings.450cab1a.chunk.js +18 -0
- package/build/upload.e2034370.chunk.js +64 -0
- package/build/users-advanced-settings-page.0c0b8230.chunk.js +13 -0
- package/build/users-email-settings-page.3126ff8c.chunk.js +28 -0
- package/build/users-providers-settings-page.b7b602e2.chunk.js +33 -0
- package/build/users-roles-settings-page.ce5b582d.chunk.js +30 -0
- package/build/webhook-edit-page.1215a6b7.chunk.js +75 -0
- package/build/{webhook-list-page.42533b59.chunk.js → webhook-list-page.b87821f2.chunk.js} +1 -1
- package/ee/server/services/passport/provider-registry.js +1 -1
- package/package.json +15 -15
- package/utils/get-plugins-path.js +17 -3
- package/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js +0 -122
- package/admin/src/content-manager/components/RepeatableComponent/AddFieldButton.js +0 -58
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js +0 -72
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/IconButtonCustoms.js +0 -32
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +0 -322
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/connect.js +0 -11
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/index.js +0 -2
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js +0 -30
- package/admin/src/content-manager/components/RepeatableComponent/utils/connect.js +0 -11
- package/admin/src/content-manager/components/RepeatableComponent/utils/select.js +0 -12
- package/admin/src/content-manager/hooks/__test__/usePrev.test.js +0 -26
- package/build/2438.afe24949.chunk.js +0 -2525
- package/build/2517.5cc235ba.chunk.js +0 -117
- package/build/4306.53359960.chunk.js +0 -98
- package/build/8881.bfdb6877.chunk.js +0 -245
- package/build/9707.932a3c12.chunk.js +0 -70
- package/build/Admin-authenticatedApp.cfc3b4c9.chunk.js +0 -80
- package/build/admin-app.ee1211cb.chunk.js +0 -112
- package/build/api-tokens-create-page.4ca2778d.chunk.js +0 -1
- package/build/api-tokens-edit-page.70a791c2.chunk.js +0 -1
- package/build/content-manager.794d3373.chunk.js +0 -1200
- package/build/content-type-builder-list-view.95012cf0.chunk.js +0 -201
- package/build/content-type-builder.95b9d6a2.chunk.js +0 -145
- package/build/email-settings-page.4bb3606f.chunk.js +0 -15
- package/build/main.a6470578.js +0 -2031
- package/build/ru-json.d7cfc2ff.chunk.js +0 -1
- package/build/upload-settings.3010911f.chunk.js +0 -18
- package/build/upload.9f19f2e8.chunk.js +0 -64
- package/build/users-advanced-settings-page.9df41d67.chunk.js +0 -13
- package/build/users-email-settings-page.56d82eaf.chunk.js +0 -28
- package/build/users-providers-settings-page.96bb7da0.chunk.js +0 -33
- package/build/users-roles-settings-page.445e5e16.chunk.js +0 -30
- package/build/webhook-edit-page.c5efc08b.chunk.js +0 -75
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
import React, { memo, useCallback, useMemo, useState } from 'react';
|
|
2
1
|
/* eslint-disable import/no-cycle */
|
|
3
|
-
import {
|
|
2
|
+
import React, { memo, useMemo, useState } from 'react';
|
|
4
3
|
import { useIntl } from 'react-intl';
|
|
5
4
|
import styled from 'styled-components';
|
|
6
5
|
import PropTypes from 'prop-types';
|
|
7
6
|
import get from 'lodash/get';
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
import { Flex } from '@strapi/design-system
|
|
11
|
-
import {
|
|
12
|
-
|
|
7
|
+
|
|
8
|
+
import { useNotification, useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
9
|
+
import { Box, Flex, TextButton, VisuallyHidden } from '@strapi/design-system';
|
|
10
|
+
import { Plus } from '@strapi/icons';
|
|
11
|
+
|
|
13
12
|
import { getMaxTempKey, getTrad } from '../../utils';
|
|
14
13
|
import { useContentTypeLayout } from '../../hooks';
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
import ComponentInitializer from '../ComponentInitializer';
|
|
17
|
-
import
|
|
18
|
-
import
|
|
16
|
+
import Component from './components/Component';
|
|
17
|
+
import * as Accordion from './components/Accordion';
|
|
18
|
+
|
|
19
19
|
import getComponentErrorKeys from './utils/getComponentErrorKeys';
|
|
20
|
-
import DraggedItem from './DraggedItem';
|
|
21
|
-
import AccordionGroupCustom from './AccordionGroupCustom';
|
|
22
20
|
|
|
23
21
|
const TextButtonCustom = styled(TextButton)`
|
|
24
22
|
height: 100%;
|
|
@@ -33,8 +31,6 @@ const TextButtonCustom = styled(TextButton)`
|
|
|
33
31
|
`;
|
|
34
32
|
|
|
35
33
|
const RepeatableComponent = ({
|
|
36
|
-
addRepeatableComponentToField,
|
|
37
|
-
formErrors,
|
|
38
34
|
componentUid,
|
|
39
35
|
componentValue,
|
|
40
36
|
componentValueLength,
|
|
@@ -43,11 +39,12 @@ const RepeatableComponent = ({
|
|
|
43
39
|
min,
|
|
44
40
|
name,
|
|
45
41
|
}) => {
|
|
42
|
+
const { addRepeatableComponentToField, formErrors, moveComponentField } =
|
|
43
|
+
useCMEditViewDataManager();
|
|
46
44
|
const toggleNotification = useNotification();
|
|
47
45
|
const { formatMessage } = useIntl();
|
|
48
46
|
const [collapseToOpen, setCollapseToOpen] = useState('');
|
|
49
|
-
const [
|
|
50
|
-
const [, drop] = useDrop({ accept: ItemTypes.COMPONENT });
|
|
47
|
+
const [liveText, setLiveText] = useState('');
|
|
51
48
|
const { getComponentLayout, components } = useContentTypeLayout();
|
|
52
49
|
const componentLayoutData = useMemo(
|
|
53
50
|
() => getComponentLayout(componentUid),
|
|
@@ -60,15 +57,15 @@ const RepeatableComponent = ({
|
|
|
60
57
|
|
|
61
58
|
const componentErrorKeys = getComponentErrorKeys(name, formErrors);
|
|
62
59
|
|
|
63
|
-
const toggleCollapses = () => {
|
|
64
|
-
setCollapseToOpen('');
|
|
65
|
-
};
|
|
66
|
-
|
|
67
60
|
const missingComponentsValue = min - componentValueLength;
|
|
68
61
|
|
|
69
62
|
const hasMinError = get(formErrors, name, { id: '' }).id.includes('min');
|
|
70
63
|
|
|
71
|
-
const
|
|
64
|
+
const toggleCollapses = () => {
|
|
65
|
+
setCollapseToOpen('');
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleClick = () => {
|
|
72
69
|
if (!isReadOnly) {
|
|
73
70
|
if (componentValueLength < max) {
|
|
74
71
|
const shouldCheckErrors = hasMinError;
|
|
@@ -83,18 +80,89 @@ const RepeatableComponent = ({
|
|
|
83
80
|
});
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const handleMoveComponentField = (newIndex, currentIndex) => {
|
|
86
|
+
setLiveText(
|
|
87
|
+
formatMessage(
|
|
88
|
+
{
|
|
89
|
+
id: getTrad('dnd.reorder'),
|
|
90
|
+
defaultMessage: '{item}, moved. New position in list: {position}.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
item: `${name}.${currentIndex}`,
|
|
94
|
+
position: getItemPos(newIndex),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
moveComponentField({
|
|
100
|
+
name,
|
|
101
|
+
newIndex,
|
|
102
|
+
currentIndex,
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const mainField = get(componentLayoutData, ['settings', 'mainField'], 'id');
|
|
107
|
+
|
|
108
|
+
const handleToggle = (key) => () => {
|
|
109
|
+
if (collapseToOpen === key) {
|
|
110
|
+
setCollapseToOpen('');
|
|
111
|
+
} else {
|
|
112
|
+
setCollapseToOpen(key);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
*
|
|
118
|
+
* @param {number} index
|
|
119
|
+
* @returns {string}
|
|
120
|
+
*/
|
|
121
|
+
const getItemPos = (index) => `${index + 1} of ${componentValueLength}`;
|
|
122
|
+
|
|
123
|
+
const handleCancel = (index) => {
|
|
124
|
+
setLiveText(
|
|
125
|
+
formatMessage(
|
|
126
|
+
{
|
|
127
|
+
id: getTrad('dnd.cancel-item'),
|
|
128
|
+
defaultMessage: '{item}, dropped. Re-order cancelled.',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
item: `${name}.${index}`,
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const handleGrabItem = (index) => {
|
|
138
|
+
setLiveText(
|
|
139
|
+
formatMessage(
|
|
140
|
+
{
|
|
141
|
+
id: getTrad('dnd.grab-item'),
|
|
142
|
+
defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
item: `${name}.${index}`,
|
|
146
|
+
position: getItemPos(index),
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const handleDropItem = (index) => {
|
|
153
|
+
setLiveText(
|
|
154
|
+
formatMessage(
|
|
155
|
+
{
|
|
156
|
+
id: getTrad('dnd.drop-item'),
|
|
157
|
+
defaultMessage: `{item}, dropped. Final position in list: {position}.`,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
item: `${name}.${index}`,
|
|
161
|
+
position: getItemPos(index),
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
};
|
|
98
166
|
|
|
99
167
|
let errorMessage = formErrors[name];
|
|
100
168
|
|
|
@@ -105,6 +173,11 @@ const RepeatableComponent = ({
|
|
|
105
173
|
'There {number, plural, =0 {are # missing components} one {is # missing component} other {are # missing components}}',
|
|
106
174
|
values: { number: missingComponentsValue },
|
|
107
175
|
};
|
|
176
|
+
} else if (componentErrorKeys.some((error) => error.split('.').length > 1) && !hasMinError) {
|
|
177
|
+
errorMessage = {
|
|
178
|
+
id: getTrad('components.RepeatableComponent.error-message'),
|
|
179
|
+
defaultMessage: 'The component(s) contain error(s)',
|
|
180
|
+
};
|
|
108
181
|
}
|
|
109
182
|
|
|
110
183
|
if (componentValueLength === 0) {
|
|
@@ -113,22 +186,44 @@ const RepeatableComponent = ({
|
|
|
113
186
|
);
|
|
114
187
|
}
|
|
115
188
|
|
|
116
|
-
const
|
|
117
|
-
(error) => error.split('.').length > 1
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
if (doesRepComponentHasChildError && !hasMinError) {
|
|
121
|
-
errorMessage = {
|
|
122
|
-
id: getTrad('components.RepeatableComponent.error-message'),
|
|
123
|
-
defaultMessage: 'The component(s) contain error(s)',
|
|
124
|
-
};
|
|
125
|
-
}
|
|
189
|
+
const ariaDescriptionId = `${name}-item-instructions`;
|
|
126
190
|
|
|
127
191
|
return (
|
|
128
|
-
<Box hasRadius
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
192
|
+
<Box hasRadius>
|
|
193
|
+
<VisuallyHidden id={ariaDescriptionId}>
|
|
194
|
+
{formatMessage({
|
|
195
|
+
id: getTrad('dnd.instructions'),
|
|
196
|
+
defaultMessage: `Press spacebar to grab and re-order`,
|
|
197
|
+
})}
|
|
198
|
+
</VisuallyHidden>
|
|
199
|
+
<VisuallyHidden aria-live="assertive">{liveText}</VisuallyHidden>
|
|
200
|
+
<Accordion.Group error={errorMessage} ariaDescribedBy={ariaDescriptionId}>
|
|
201
|
+
<Accordion.Content aria-describedby={ariaDescriptionId}>
|
|
202
|
+
{componentValue.map((data, index) => {
|
|
203
|
+
const key = data.__temp_key__;
|
|
204
|
+
const componentFieldName = `${name}.${index}`;
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<Component
|
|
208
|
+
componentFieldName={componentFieldName}
|
|
209
|
+
componentUid={componentUid}
|
|
210
|
+
fields={componentLayoutData.layouts.edit}
|
|
211
|
+
key={key}
|
|
212
|
+
index={index}
|
|
213
|
+
isOpen={collapseToOpen === key}
|
|
214
|
+
isReadOnly={isReadOnly}
|
|
215
|
+
mainField={mainField}
|
|
216
|
+
moveComponentField={handleMoveComponentField}
|
|
217
|
+
onClickToggle={handleToggle(key)}
|
|
218
|
+
toggleCollapses={toggleCollapses}
|
|
219
|
+
onCancel={handleCancel}
|
|
220
|
+
onDropItem={handleDropItem}
|
|
221
|
+
onGrabItem={handleGrabItem}
|
|
222
|
+
/>
|
|
223
|
+
);
|
|
224
|
+
})}
|
|
225
|
+
</Accordion.Content>
|
|
226
|
+
<Accordion.Footer>
|
|
132
227
|
<Flex justifyContent="center" height="48px" background="neutral0">
|
|
133
228
|
<TextButtonCustom disabled={isReadOnly} onClick={handleClick} startIcon={<Plus />}>
|
|
134
229
|
{formatMessage({
|
|
@@ -137,39 +232,8 @@ const RepeatableComponent = ({
|
|
|
137
232
|
})}
|
|
138
233
|
</TextButtonCustom>
|
|
139
234
|
</Flex>
|
|
140
|
-
|
|
141
|
-
>
|
|
142
|
-
{componentValue.map((data, index) => {
|
|
143
|
-
const key = data.__temp_key__;
|
|
144
|
-
const isOpen = collapseToOpen === key;
|
|
145
|
-
const componentFieldName = `${name}.${index}`;
|
|
146
|
-
const hasErrors = componentErrorKeys.includes(componentFieldName);
|
|
147
|
-
|
|
148
|
-
return (
|
|
149
|
-
<DraggedItem
|
|
150
|
-
componentFieldName={componentFieldName}
|
|
151
|
-
componentUid={componentUid}
|
|
152
|
-
hasErrors={hasErrors}
|
|
153
|
-
hasMinError={hasMinError}
|
|
154
|
-
isDraggingSibling={isDraggingSibling}
|
|
155
|
-
isOpen={isOpen}
|
|
156
|
-
isReadOnly={isReadOnly}
|
|
157
|
-
key={key}
|
|
158
|
-
onClickToggle={() => {
|
|
159
|
-
if (isOpen) {
|
|
160
|
-
setCollapseToOpen('');
|
|
161
|
-
} else {
|
|
162
|
-
setCollapseToOpen(key);
|
|
163
|
-
}
|
|
164
|
-
}}
|
|
165
|
-
parentName={name}
|
|
166
|
-
schema={componentLayoutData}
|
|
167
|
-
setIsDraggingSibling={setIsDraggingSibling}
|
|
168
|
-
toggleCollapses={toggleCollapses}
|
|
169
|
-
/>
|
|
170
|
-
);
|
|
171
|
-
})}
|
|
172
|
-
</AccordionGroupCustom>
|
|
235
|
+
</Accordion.Footer>
|
|
236
|
+
</Accordion.Group>
|
|
173
237
|
</Box>
|
|
174
238
|
);
|
|
175
239
|
};
|
|
@@ -177,25 +241,21 @@ const RepeatableComponent = ({
|
|
|
177
241
|
RepeatableComponent.defaultProps = {
|
|
178
242
|
componentValue: null,
|
|
179
243
|
componentValueLength: 0,
|
|
180
|
-
|
|
244
|
+
isReadOnly: false,
|
|
181
245
|
max: Infinity,
|
|
182
246
|
min: 0,
|
|
183
247
|
};
|
|
184
248
|
|
|
185
249
|
RepeatableComponent.propTypes = {
|
|
186
|
-
addRepeatableComponentToField: PropTypes.func.isRequired,
|
|
187
250
|
componentUid: PropTypes.string.isRequired,
|
|
188
251
|
componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
|
189
252
|
componentValueLength: PropTypes.number,
|
|
190
|
-
|
|
191
|
-
isReadOnly: PropTypes.bool.isRequired,
|
|
253
|
+
isReadOnly: PropTypes.bool,
|
|
192
254
|
max: PropTypes.number,
|
|
193
255
|
min: PropTypes.number,
|
|
194
256
|
name: PropTypes.string.isRequired,
|
|
195
257
|
};
|
|
196
258
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
export default connect(Memoized, select);
|
|
259
|
+
export default memo(RepeatableComponent);
|
|
200
260
|
|
|
201
261
|
export { RepeatableComponent };
|
|
@@ -103,7 +103,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
|
103
103
|
setIsCreatingEntry(true);
|
|
104
104
|
|
|
105
105
|
try {
|
|
106
|
-
const { data } = await axiosInstance(getRequestUrl(`${slug}${searchToSend}`), {
|
|
106
|
+
const { data } = await axiosInstance.get(getRequestUrl(`${slug}${searchToSend}`), {
|
|
107
107
|
cancelToken: source.token,
|
|
108
108
|
});
|
|
109
109
|
|
|
@@ -43,7 +43,7 @@ const Editor = ({
|
|
|
43
43
|
}, [editorRef, textareaRef, name, placeholder]);
|
|
44
44
|
|
|
45
45
|
useEffect(() => {
|
|
46
|
-
if (value && !editorRef.current.
|
|
46
|
+
if (value && !editorRef.current.hasFocus()) {
|
|
47
47
|
editorRef.current.setValue(value);
|
|
48
48
|
}
|
|
49
49
|
}, [editorRef, value]);
|
|
@@ -6,3 +6,5 @@ export { default as usePluginsQueryParams } from './usePluginsQueryParams';
|
|
|
6
6
|
export { default as useSyncRbac } from './useSyncRbac';
|
|
7
7
|
export { default as useWysiwyg } from './useWysiwyg';
|
|
8
8
|
export { usePrev } from './usePrev';
|
|
9
|
+
export { useDragAndDrop } from './useDragAndDrop';
|
|
10
|
+
export { useKeyboardDragAndDrop } from './useKeyboardDragAndDrop';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
import { useDrag, useDrop } from 'react-dnd';
|
|
3
|
+
|
|
4
|
+
import { useKeyboardDragAndDrop } from './useKeyboardDragAndDrop';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef UseDragAndDropOptions
|
|
8
|
+
*
|
|
9
|
+
* @type {{
|
|
10
|
+
* type?: string,
|
|
11
|
+
* index: number,
|
|
12
|
+
* item?: object,
|
|
13
|
+
* onStart?: () => void,
|
|
14
|
+
* onEnd?: () => void,
|
|
15
|
+
* } & import('./useKeyboardDragAndDrop').UseKeyboardDragAndDropCallbacks}
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef UseDragAndDropReturn
|
|
20
|
+
*
|
|
21
|
+
* @type {[props: {handlerId: import('dnd-core').Identifier, isDragging: boolean, handleKeyDown: (event: import('react').KeyboardEvent<HTMLButtonElement>) => void}, objectRef: React.RefObject<HTMLElement>, dropRef: import('react-dnd').ConnectDropTarget, dragRef: import('react-dnd').ConnectDragSource, dragPreviewRef: import('react-dnd').ConnectDragPreview]}
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A utility hook abstracting the general drag and drop hooks from react-dnd.
|
|
26
|
+
* Centralising the same behaviours and by default offering keyboard support.
|
|
27
|
+
*
|
|
28
|
+
* @type {(active: boolean, options: UseDragAndDropOptions) => UseDragAndDropReturn}
|
|
29
|
+
*/
|
|
30
|
+
export const useDragAndDrop = (
|
|
31
|
+
active,
|
|
32
|
+
{
|
|
33
|
+
type = 'STRAPI_DND',
|
|
34
|
+
index,
|
|
35
|
+
item = {},
|
|
36
|
+
onStart,
|
|
37
|
+
onEnd,
|
|
38
|
+
onGrabItem,
|
|
39
|
+
onDropItem,
|
|
40
|
+
onCancel,
|
|
41
|
+
onMoveItem,
|
|
42
|
+
}
|
|
43
|
+
) => {
|
|
44
|
+
const objectRef = useRef(null);
|
|
45
|
+
|
|
46
|
+
const [{ handlerId }, dropRef] = useDrop({
|
|
47
|
+
accept: type,
|
|
48
|
+
collect(monitor) {
|
|
49
|
+
return {
|
|
50
|
+
handlerId: monitor.getHandlerId(),
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
hover(item, monitor) {
|
|
54
|
+
if (!objectRef.current) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const dragIndex = item.index;
|
|
58
|
+
const newInd = index;
|
|
59
|
+
|
|
60
|
+
// Don't replace items with themselves
|
|
61
|
+
if (dragIndex === newInd) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const hoverBoundingRect = objectRef.current.getBoundingClientRect();
|
|
66
|
+
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
67
|
+
const clientOffset = monitor.getClientOffset();
|
|
68
|
+
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
|
69
|
+
|
|
70
|
+
// Dragging downwards
|
|
71
|
+
if (dragIndex < newInd && hoverClientY < hoverMiddleY) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Dragging upwards
|
|
76
|
+
if (dragIndex > newInd && hoverClientY > hoverMiddleY) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Time to actually perform the action
|
|
81
|
+
onMoveItem(newInd, dragIndex);
|
|
82
|
+
item.index = newInd;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const [{ isDragging }, dragRef, dragPreviewRef] = useDrag({
|
|
87
|
+
type,
|
|
88
|
+
item() {
|
|
89
|
+
if (onStart) {
|
|
90
|
+
onStart();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* This will be attached and it helps define the preview sizes
|
|
95
|
+
* when a component is flexy e.g. Relations
|
|
96
|
+
*/
|
|
97
|
+
const { width } = objectRef.current?.getBoundingClientRect() ?? {};
|
|
98
|
+
|
|
99
|
+
return { index, width, ...item };
|
|
100
|
+
},
|
|
101
|
+
end() {
|
|
102
|
+
if (onEnd) {
|
|
103
|
+
onEnd();
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
canDrag: active,
|
|
107
|
+
collect: (monitor) => ({
|
|
108
|
+
isDragging: monitor.isDragging(),
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const handleKeyDown = useKeyboardDragAndDrop(index, {
|
|
113
|
+
onGrabItem,
|
|
114
|
+
onDropItem,
|
|
115
|
+
onCancel,
|
|
116
|
+
onMoveItem,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return [{ handlerId, isDragging, handleKeyDown }, objectRef, dropRef, dragRef, dragPreviewRef];
|
|
120
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef UseKeyboardDragAndDropCallbacks
|
|
5
|
+
*
|
|
6
|
+
* @type {{
|
|
7
|
+
* onCancel?: (index: number) => void,
|
|
8
|
+
* onDropItem?: (index: number) => void,
|
|
9
|
+
* onGrabItem?: (index: number) => void,
|
|
10
|
+
* onMoveItem: (newIndex: number, currentIndex: number) => void,
|
|
11
|
+
* }}
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Utility hook designed to implement keyboard accessibile drag and drop by
|
|
16
|
+
* returning an onKeyDown handler to be passed to the drag icon button.
|
|
17
|
+
*
|
|
18
|
+
* @internal - You should use `useDragAndDrop` instead.
|
|
19
|
+
*
|
|
20
|
+
* @type {(index: number, callbacks: UseKeyboardDragAndDropCallbacks) => (event: React.KeyboardEvent<HTMLButtonElement>) => void}
|
|
21
|
+
*/
|
|
22
|
+
export const useKeyboardDragAndDrop = (index, { onCancel, onDropItem, onGrabItem, onMoveItem }) => {
|
|
23
|
+
const [isSelected, setIsSelected] = useState(false);
|
|
24
|
+
/**
|
|
25
|
+
* @type {(movement: 'UP' | 'DOWN') => void})}
|
|
26
|
+
*/
|
|
27
|
+
const handleMove = (movement) => {
|
|
28
|
+
if (!isSelected) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (movement === 'UP') {
|
|
33
|
+
onMoveItem(index - 1, index);
|
|
34
|
+
} else if (movement === 'DOWN') {
|
|
35
|
+
onMoveItem(index + 1, index);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handleDragClick = () => {
|
|
40
|
+
if (isSelected) {
|
|
41
|
+
if (onDropItem) {
|
|
42
|
+
onDropItem(index);
|
|
43
|
+
}
|
|
44
|
+
setIsSelected(false);
|
|
45
|
+
} else {
|
|
46
|
+
if (onGrabItem) {
|
|
47
|
+
onGrabItem(index);
|
|
48
|
+
}
|
|
49
|
+
setIsSelected(true);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const handleCancel = () => {
|
|
54
|
+
if (isSelected) {
|
|
55
|
+
setIsSelected(false);
|
|
56
|
+
|
|
57
|
+
if (onCancel) {
|
|
58
|
+
onCancel(index);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @type {React.KeyboardEventHandler<HTMLButtonElement>}
|
|
65
|
+
*/
|
|
66
|
+
const handleKeyDown = (e) => {
|
|
67
|
+
if (e.key === 'Tab' && !isSelected) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
|
|
73
|
+
switch (e.key) {
|
|
74
|
+
case ' ':
|
|
75
|
+
case 'Enter':
|
|
76
|
+
handleDragClick();
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'Escape':
|
|
80
|
+
handleCancel();
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
case 'ArrowDown':
|
|
84
|
+
case 'ArrowRight':
|
|
85
|
+
handleMove('DOWN');
|
|
86
|
+
break;
|
|
87
|
+
|
|
88
|
+
case 'ArrowUp':
|
|
89
|
+
case 'ArrowLeft':
|
|
90
|
+
handleMove('UP');
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
default:
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return handleKeyDown;
|
|
98
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { useCustomFields } from '@strapi/helper-plugin';
|
|
3
|
+
|
|
4
|
+
const componentStore = new Map();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @description
|
|
8
|
+
* A hook to lazy load custom field components
|
|
9
|
+
* @param {Array.<string>} componentUids - The uids to look up components
|
|
10
|
+
* @returns object
|
|
11
|
+
*/
|
|
12
|
+
const useLazyComponents = (componentUids = []) => {
|
|
13
|
+
const [lazyComponentStore, setLazyComponentStore] = useState(Object.fromEntries(componentStore));
|
|
14
|
+
const [loading, setLoading] = useState(() => {
|
|
15
|
+
if (componentStore.size === 0 && componentUids.length > 0) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return false;
|
|
20
|
+
});
|
|
21
|
+
const customFieldsRegistry = useCustomFields();
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const setStore = (store) => {
|
|
25
|
+
setLazyComponentStore(store);
|
|
26
|
+
setLoading(false);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const lazyLoadComponents = async (uids, components) => {
|
|
30
|
+
const modules = await Promise.all(components);
|
|
31
|
+
|
|
32
|
+
uids.forEach((uid, index) => {
|
|
33
|
+
componentStore.set(uid, modules[index].default);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
setStore(Object.fromEntries(componentStore));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (componentUids.length && loading) {
|
|
40
|
+
/**
|
|
41
|
+
* These uids are not in the component store therefore we need to get the components
|
|
42
|
+
*/
|
|
43
|
+
const newUids = componentUids.filter((uid) => !componentStore.get(uid));
|
|
44
|
+
|
|
45
|
+
const componentPromises = newUids.map((uid) => {
|
|
46
|
+
const customField = customFieldsRegistry.get(uid);
|
|
47
|
+
|
|
48
|
+
return customField.components.Input();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (componentPromises.length > 0) {
|
|
52
|
+
lazyLoadComponents(newUids, componentPromises);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}, [componentUids, customFieldsRegistry, loading]);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Wrap this in a callback so it can be used in
|
|
59
|
+
* effects to cleanup the cached store if required
|
|
60
|
+
*/
|
|
61
|
+
const cleanup = useCallback(() => {
|
|
62
|
+
componentStore.clear();
|
|
63
|
+
setLazyComponentStore({});
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
return { isLazyLoading: loading, lazyComponentStore, cleanup };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default useLazyComponents;
|
package/admin/src/content-manager/pages/CollectionTypeRecursivePath/components/ErrorFallback.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AnErrorOccurred } from '@strapi/helper-plugin';
|
|
3
|
+
import { Box } from '@strapi/design-system/Box';
|
|
4
|
+
|
|
5
|
+
const ErrorFallback = () => {
|
|
6
|
+
return (
|
|
7
|
+
<Box padding={8}>
|
|
8
|
+
<AnErrorOccurred />
|
|
9
|
+
</Box>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default ErrorFallback;
|
|
@@ -3,7 +3,7 @@ import { Switch, Route } from 'react-router-dom';
|
|
|
3
3
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
4
4
|
import { get } from 'lodash';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
|
-
import {
|
|
6
|
+
import { LoadingIndicatorPage, CheckPagePermissions } from '@strapi/helper-plugin';
|
|
7
7
|
import permissions from '../../../permissions';
|
|
8
8
|
import { ContentTypeLayoutContext } from '../../contexts';
|
|
9
9
|
import { useFetchContentTypeLayout } from '../../hooks';
|
|
@@ -12,6 +12,7 @@ import EditViewLayoutManager from '../EditViewLayoutManager';
|
|
|
12
12
|
import EditSettingsView from '../EditSettingsView';
|
|
13
13
|
import ListViewLayout from '../ListViewLayoutManager';
|
|
14
14
|
import ListSettingsView from '../ListSettingsView';
|
|
15
|
+
import ErrorFallback from './components/ErrorFallback';
|
|
15
16
|
|
|
16
17
|
const cmPermissions = permissions.contentManager;
|
|
17
18
|
|