@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.
Files changed (132) hide show
  1. package/admin/src/content-manager/components/ComponentInitializer/index.js +1 -7
  2. package/admin/src/content-manager/components/{RepeatableComponent/DragPreview.js → DragLayer/ComponentDragPreview.js} +25 -12
  3. package/admin/src/content-manager/components/DragLayer/RelationDragPreview.js +75 -0
  4. package/admin/src/content-manager/components/DragLayer/index.js +23 -7
  5. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +130 -84
  6. package/admin/src/content-manager/components/DynamicZone/index.js +99 -24
  7. package/admin/src/content-manager/components/DynamicZone/utils/select.js +9 -5
  8. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +76 -14
  9. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +23 -23
  10. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +24 -5
  11. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +8 -1
  12. package/admin/src/content-manager/components/Inputs/index.js +5 -19
  13. package/admin/src/content-manager/components/NonRepeatableComponent/index.js +4 -0
  14. package/admin/src/content-manager/components/RelationInput/RelationInput.js +203 -63
  15. package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +134 -21
  16. package/admin/src/content-manager/components/RelationInput/components/RelationList.js +1 -2
  17. package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
  18. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +132 -10
  19. package/admin/src/content-manager/components/RepeatableComponent/components/Accordion.js +77 -0
  20. package/admin/src/content-manager/components/RepeatableComponent/components/Component.js +262 -0
  21. package/admin/src/content-manager/components/RepeatableComponent/{DraggedItem → components}/Preview.js +0 -0
  22. package/admin/src/content-manager/components/RepeatableComponent/index.js +147 -87
  23. package/admin/src/content-manager/components/RepeatableComponent/utils/getComponentErrorKeys.js +1 -1
  24. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +1 -1
  25. package/admin/src/content-manager/components/Wysiwyg/Editor.js +1 -1
  26. package/admin/src/content-manager/hooks/index.js +2 -0
  27. package/admin/src/content-manager/hooks/useDragAndDrop.js +120 -0
  28. package/admin/src/content-manager/hooks/useKeyboardDragAndDrop.js +98 -0
  29. package/admin/src/content-manager/hooks/useLazyComponents/index.js +69 -0
  30. package/admin/src/content-manager/pages/CollectionTypeRecursivePath/components/ErrorFallback.js +13 -0
  31. package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +2 -1
  32. package/admin/src/content-manager/pages/EditView/GridRow/index.js +62 -0
  33. package/admin/src/content-manager/pages/EditView/index.js +74 -154
  34. package/admin/src/content-manager/pages/EditView/selectors.js +14 -0
  35. package/admin/src/content-manager/pages/EditView/utils/createAttributesLayout.js +11 -6
  36. package/admin/src/content-manager/pages/EditView/utils/getCustomFieldUidsFromLayout.js +18 -0
  37. package/admin/src/content-manager/pages/EditView/utils/index.js +1 -0
  38. package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +1 -1
  39. package/admin/src/content-manager/sharedReducers/crudReducer/actions.js +5 -0
  40. package/admin/src/content-manager/sharedReducers/crudReducer/constants.js +2 -0
  41. package/admin/src/content-manager/sharedReducers/crudReducer/reducer.js +7 -0
  42. package/admin/src/content-manager/utils/ItemTypes.js +1 -1
  43. package/admin/src/content-manager/utils/composeRefs.js +28 -0
  44. package/admin/src/content-manager/utils/getMaxTempKey.js +1 -1
  45. package/admin/src/content-manager/utils/index.js +7 -0
  46. package/admin/src/core/utils/axiosInstance.js +4 -2
  47. package/admin/src/hooks/index.js +1 -0
  48. package/admin/src/hooks/useFetchClient/index.js +23 -0
  49. package/admin/src/pages/HomePage/SocialLinks.js +4 -4
  50. package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +23 -18
  51. package/admin/src/translations/en.json +6 -0
  52. package/admin/src/translations/ru.json +789 -489
  53. package/admin/src/utils/fetchClient.js +45 -0
  54. package/admin/src/utils/getFetchClient.js +10 -0
  55. package/build/4306.df40a798.chunk.js +98 -0
  56. package/build/{4318.daf31770.chunk.js → 4318.80bdf035.chunk.js} +2 -2
  57. package/build/5057.195a59ff.chunk.js +65 -0
  58. package/build/{805.a1894307.chunk.js → 805.e991a370.chunk.js} +6 -6
  59. package/build/{4986.3820d11d.chunk.js → 8176.b19bc128.chunk.js} +32 -32
  60. package/build/{1233.32d6888d.chunk.js → 8186.55910742.chunk.js} +94 -94
  61. package/build/{8633.8da5488a.chunk.js → 8633.59223842.chunk.js} +1 -1
  62. package/build/8881.c693411a.chunk.js +245 -0
  63. package/build/9161.4a0ab137.chunk.js +2119 -0
  64. package/build/9279.6290c87a.chunk.js +117 -0
  65. package/build/9707.a0cc4ad8.chunk.js +70 -0
  66. package/build/Admin-authenticatedApp.f9e74dc0.chunk.js +80 -0
  67. package/build/{Admin_homePage.b4db4df8.chunk.js → Admin_homePage.8945f71a.chunk.js} +5 -5
  68. package/build/{Admin_marketplace.fa51405b.chunk.js → Admin_marketplace.ed754a4a.chunk.js} +1 -1
  69. package/build/{Admin_pluginsPage.14d2840f.chunk.js → Admin_pluginsPage.3c872de7.chunk.js} +1 -1
  70. package/build/{Admin_profilePage.6c2c8398.chunk.js → Admin_profilePage.c07bdf08.chunk.js} +1 -1
  71. package/build/{Admin_settingsPage.5e740514.chunk.js → Admin_settingsPage.50a8765b.chunk.js} +5 -5
  72. package/build/admin-app.2861b6d2.chunk.js +112 -0
  73. package/build/{admin-edit-roles-page.c7c338b3.chunk.js → admin-edit-roles-page.f407538c.chunk.js} +1 -1
  74. package/build/{admin-edit-users.d254c128.chunk.js → admin-edit-users.85231e4c.chunk.js} +1 -1
  75. package/build/{admin-users.7c423e41.chunk.js → admin-users.a2707644.chunk.js} +2 -2
  76. package/build/api-tokens-create-page.dd4ddfcb.chunk.js +1 -0
  77. package/build/api-tokens-edit-page.821c5a6c.chunk.js +1 -0
  78. package/build/{api-tokens-list-page.fe994b6b.chunk.js → api-tokens-list-page.700e575f.chunk.js} +1 -1
  79. package/build/content-manager.ee948f75.chunk.js +1186 -0
  80. package/build/content-type-builder-list-view.4412efc3.chunk.js +201 -0
  81. package/build/content-type-builder.b132b5f4.chunk.js +145 -0
  82. package/build/email-settings-page.db0d98d1.chunk.js +15 -0
  83. package/build/{en-json.7dd57947.chunk.js → en-json.4a56dca7.chunk.js} +1 -1
  84. package/build/index.html +1 -1
  85. package/build/main.faac89ee.js +2025 -0
  86. package/build/ru-json.8830286f.chunk.js +1 -0
  87. package/build/{runtime~main.6e7d95b9.js → runtime~main.75a15b8e.js} +1 -1
  88. package/build/{sso-settings-page.eb30a02e.chunk.js → sso-settings-page.adb12ac3.chunk.js} +1 -1
  89. package/build/upload-settings.450cab1a.chunk.js +18 -0
  90. package/build/upload.e2034370.chunk.js +64 -0
  91. package/build/users-advanced-settings-page.0c0b8230.chunk.js +13 -0
  92. package/build/users-email-settings-page.3126ff8c.chunk.js +28 -0
  93. package/build/users-providers-settings-page.b7b602e2.chunk.js +33 -0
  94. package/build/users-roles-settings-page.ce5b582d.chunk.js +30 -0
  95. package/build/webhook-edit-page.1215a6b7.chunk.js +75 -0
  96. package/build/{webhook-list-page.42533b59.chunk.js → webhook-list-page.b87821f2.chunk.js} +1 -1
  97. package/ee/server/services/passport/provider-registry.js +1 -1
  98. package/package.json +15 -15
  99. package/utils/get-plugins-path.js +17 -3
  100. package/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js +0 -122
  101. package/admin/src/content-manager/components/RepeatableComponent/AddFieldButton.js +0 -58
  102. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js +0 -72
  103. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/IconButtonCustoms.js +0 -32
  104. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +0 -322
  105. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/connect.js +0 -11
  106. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/index.js +0 -2
  107. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js +0 -30
  108. package/admin/src/content-manager/components/RepeatableComponent/utils/connect.js +0 -11
  109. package/admin/src/content-manager/components/RepeatableComponent/utils/select.js +0 -12
  110. package/admin/src/content-manager/hooks/__test__/usePrev.test.js +0 -26
  111. package/build/2438.afe24949.chunk.js +0 -2525
  112. package/build/2517.5cc235ba.chunk.js +0 -117
  113. package/build/4306.53359960.chunk.js +0 -98
  114. package/build/8881.bfdb6877.chunk.js +0 -245
  115. package/build/9707.932a3c12.chunk.js +0 -70
  116. package/build/Admin-authenticatedApp.cfc3b4c9.chunk.js +0 -80
  117. package/build/admin-app.ee1211cb.chunk.js +0 -112
  118. package/build/api-tokens-create-page.4ca2778d.chunk.js +0 -1
  119. package/build/api-tokens-edit-page.70a791c2.chunk.js +0 -1
  120. package/build/content-manager.794d3373.chunk.js +0 -1200
  121. package/build/content-type-builder-list-view.95012cf0.chunk.js +0 -201
  122. package/build/content-type-builder.95b9d6a2.chunk.js +0 -145
  123. package/build/email-settings-page.4bb3606f.chunk.js +0 -15
  124. package/build/main.a6470578.js +0 -2031
  125. package/build/ru-json.d7cfc2ff.chunk.js +0 -1
  126. package/build/upload-settings.3010911f.chunk.js +0 -18
  127. package/build/upload.9f19f2e8.chunk.js +0 -64
  128. package/build/users-advanced-settings-page.9df41d67.chunk.js +0 -13
  129. package/build/users-email-settings-page.56d82eaf.chunk.js +0 -28
  130. package/build/users-providers-settings-page.96bb7da0.chunk.js +0 -33
  131. package/build/users-roles-settings-page.445e5e16.chunk.js +0 -30
  132. package/build/webhook-edit-page.c5efc08b.chunk.js +0 -75
@@ -9,15 +9,20 @@ function useSelect(name) {
9
9
  isCreatingEntry,
10
10
  formErrors,
11
11
  modifiedData,
12
- moveComponentUp,
13
- moveComponentDown,
12
+ moveComponentField,
14
13
  removeComponentFromDynamicZone,
15
14
  readActionAllowedFields,
16
15
  updateActionAllowedFields,
17
16
  } = useCMEditViewDataManager();
18
17
 
19
18
  const dynamicDisplayedComponents = useMemo(
20
- () => get(modifiedData, [name], []).map((data) => data.__component),
19
+ () =>
20
+ get(modifiedData, [name], []).map((data) => {
21
+ return {
22
+ componentUid: data.__component,
23
+ id: data.id ?? data.__temp_key__,
24
+ };
25
+ }),
21
26
  [modifiedData, name]
22
27
  );
23
28
 
@@ -39,8 +44,7 @@ function useSelect(name) {
39
44
  isCreatingEntry,
40
45
  isFieldAllowed,
41
46
  isFieldReadable,
42
- moveComponentUp,
43
- moveComponentDown,
47
+ moveComponentField,
44
48
  removeComponentFromDynamicZone,
45
49
  dynamicDisplayedComponents,
46
50
  };
@@ -8,7 +8,9 @@ import set from 'lodash/set';
8
8
  import PropTypes from 'prop-types';
9
9
  import { useIntl } from 'react-intl';
10
10
  import { Prompt, Redirect } from 'react-router-dom';
11
- import { Main } from '@strapi/design-system/Main';
11
+ import { useDispatch, useSelector } from 'react-redux';
12
+
13
+ import { Main } from '@strapi/design-system';
12
14
  import {
13
15
  LoadingIndicatorPage,
14
16
  ContentManagerEditViewDataManagerContext,
@@ -20,8 +22,13 @@ import {
20
22
  } from '@strapi/helper-plugin';
21
23
 
22
24
  import { getTrad, removeKeyInObject } from '../../utils';
25
+
26
+ import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
27
+
23
28
  import reducer, { initialState } from './reducer';
24
29
  import { cleanData, createYupSchema, recursivelyFindPathsBasedOnCondition } from './utils';
30
+ import { clearSetModifiedDataOnly } from '../../sharedReducers/crudReducer/actions';
31
+ import { usePrev } from '../../hooks';
25
32
 
26
33
  const EditViewDataManagerProvider = ({
27
34
  allLayoutData,
@@ -61,6 +68,9 @@ const EditViewDataManagerProvider = ({
61
68
  publishConfirmation,
62
69
  } = reducerState;
63
70
 
71
+ const { setModifiedDataOnly } = useSelector(selectCrudReducer);
72
+ const reduxDispatch = useDispatch();
73
+
64
74
  const toggleNotification = useNotification();
65
75
  const { lockApp, unlockApp } = useOverlayBlocker();
66
76
 
@@ -144,8 +154,18 @@ const EditViewDataManagerProvider = ({
144
154
 
145
155
  const { components } = allLayoutData;
146
156
 
157
+ const previousInitialValues = usePrev(initialValues);
158
+
147
159
  useEffect(() => {
148
- if (initialValues && currentContentTypeLayout?.attributes) {
160
+ /**
161
+ * Only fire this effect if the initialValues are different
162
+ * otherwise it's a fruitless effort no matter what happens.
163
+ */
164
+ if (
165
+ initialValues &&
166
+ currentContentTypeLayout?.attributes &&
167
+ !isEqual(previousInitialValues, initialValues)
168
+ ) {
149
169
  /**
150
170
  * This will return an array of paths:
151
171
  * ['many_to_one', 'one_to_many', 'one_to_one']
@@ -179,9 +199,25 @@ const EditViewDataManagerProvider = ({
179
199
  componentPaths,
180
200
  repeatableComponentPaths,
181
201
  dynamicZonePaths,
202
+ setModifiedDataOnly,
182
203
  });
204
+
205
+ /**
206
+ * TODO: This should be moved to a side-effect e.g. thunks
207
+ * something to consider for V5
208
+ */
209
+ if (setModifiedDataOnly) {
210
+ reduxDispatch(clearSetModifiedDataOnly());
211
+ }
183
212
  }
184
- }, [initialValues, currentContentTypeLayout, components]);
213
+ }, [
214
+ initialValues,
215
+ currentContentTypeLayout,
216
+ components,
217
+ setModifiedDataOnly,
218
+ reduxDispatch,
219
+ previousInitialValues,
220
+ ]);
185
221
 
186
222
  const dispatchAddComponent = useCallback(
187
223
  (type) =>
@@ -216,7 +252,7 @@ const EditViewDataManagerProvider = ({
216
252
  /**
217
253
  * @type {({ name: string, value: Relation, toOneRelation: boolean}) => void}
218
254
  */
219
- const connectRelation = useCallback(({ name, value, toOneRelation }) => {
255
+ const relationConnect = useCallback(({ name, value, toOneRelation }) => {
220
256
  dispatch({
221
257
  type: 'CONNECT_RELATION',
222
258
  keys: name.split('.'),
@@ -225,7 +261,7 @@ const EditViewDataManagerProvider = ({
225
261
  });
226
262
  }, []);
227
263
 
228
- const loadRelation = useCallback(({ target: { name, value } }) => {
264
+ const relationLoad = useCallback(({ target: { name, value } }) => {
229
265
  dispatch({
230
266
  type: 'LOAD_RELATION',
231
267
  keys: name.split('.'),
@@ -498,16 +534,16 @@ const EditViewDataManagerProvider = ({
498
534
  [shouldCheckDZErrors]
499
535
  );
500
536
 
501
- const moveComponentField = useCallback((pathToComponent, dragIndex, hoverIndex) => {
537
+ const moveComponentField = useCallback(({ name, newIndex, currentIndex }) => {
502
538
  dispatch({
503
539
  type: 'MOVE_COMPONENT_FIELD',
504
- pathToComponent,
505
- dragIndex,
506
- hoverIndex,
540
+ keys: name.split('.'),
541
+ newIndex,
542
+ oldIndex: currentIndex,
507
543
  });
508
544
  }, []);
509
545
 
510
- const disconnectRelation = useCallback(({ name, id }) => {
546
+ const relationDisconnect = useCallback(({ name, id }) => {
511
547
  dispatch({
512
548
  type: 'DISCONNECT_RELATION',
513
549
  keys: name.split('.'),
@@ -515,6 +551,25 @@ const EditViewDataManagerProvider = ({
515
551
  });
516
552
  }, []);
517
553
 
554
+ /**
555
+ * @typedef Payload
556
+ * @type {object}
557
+ * @property {string} name - The name of the field in `modifiedData`
558
+ * @property {number} oldIndex - The relation's current index
559
+ * @property {number} newIndex - The relation's new index
560
+ *
561
+ *
562
+ * @type {(payload: Payload) => void}
563
+ */
564
+ const relationReorder = useCallback(({ name, oldIndex, newIndex }) => {
565
+ dispatch({
566
+ type: 'REORDER_RELATION',
567
+ keys: name.split('.'),
568
+ oldIndex,
569
+ newIndex,
570
+ });
571
+ }, []);
572
+
518
573
  const removeComponentFromDynamicZone = useCallback(
519
574
  (dynamicZoneName, index) => {
520
575
  trackUsageRef.current('removeComponentFromDynamicZone');
@@ -565,7 +620,6 @@ const EditViewDataManagerProvider = ({
565
620
  value={{
566
621
  addComponentToDynamicZone,
567
622
  addNonRepeatableComponentToField,
568
- connectRelation,
569
623
  addRepeatableComponentToField,
570
624
  allLayoutData,
571
625
  checkFormErrors,
@@ -578,20 +632,28 @@ const EditViewDataManagerProvider = ({
578
632
  shouldNotRunValidations,
579
633
  status,
580
634
  layout: currentContentTypeLayout,
581
- loadRelation,
582
635
  modifiedData,
583
- moveComponentDown,
584
636
  moveComponentField,
637
+ /**
638
+ * @deprecated use `moveComponentField` instead. This will be removed in v5.
639
+ */
640
+ moveComponentDown,
641
+ /**
642
+ * @deprecated use `moveComponentField` instead. This will be removed in v5.
643
+ */
585
644
  moveComponentUp,
586
645
  onChange: handleChange,
587
646
  onPublish: handlePublish,
588
647
  onUnpublish,
589
- disconnectRelation,
590
648
  readActionAllowedFields,
591
649
  redirectToPreviousPage,
592
650
  removeComponentFromDynamicZone,
593
651
  removeComponentFromField,
594
652
  removeRepeatableField,
653
+ relationConnect,
654
+ relationDisconnect,
655
+ relationLoad,
656
+ relationReorder,
595
657
  slug,
596
658
  triggerFormValidation,
597
659
  updateActionAllowedFields,
@@ -88,6 +88,7 @@ const reducer = (state, action) =>
88
88
  ? {
89
89
  ...state.componentsDataStructure[componentLayoutData.uid],
90
90
  __component: componentLayoutData.uid,
91
+ __temp_key__: getMaxTempKey(currentValue) + 1,
91
92
  }
92
93
  : {
93
94
  ...state.componentsDataStructure[componentLayoutData.uid],
@@ -172,15 +173,29 @@ const reducer = (state, action) =>
172
173
  const { id } = action;
173
174
  const modifiedDataRelation = get(state, [...path]);
174
175
 
175
- /**
176
- * TODO: before merge make this performant (e.g. 1000 relations === long time)
177
- */
178
176
  const newRelations = modifiedDataRelation.filter((rel) => rel.id !== id);
179
177
 
180
178
  set(draftState, path, newRelations);
181
179
 
182
180
  break;
183
181
  }
182
+ case 'MOVE_COMPONENT_FIELD':
183
+ case 'REORDER_RELATION': {
184
+ const { oldIndex, newIndex, keys } = action;
185
+ const path = ['modifiedData', ...keys];
186
+ const modifiedDataRelations = get(state, [...path]);
187
+
188
+ const currentItem = modifiedDataRelations[oldIndex];
189
+
190
+ const newRelations = [...modifiedDataRelations];
191
+
192
+ newRelations.splice(oldIndex, 1);
193
+ newRelations.splice(newIndex, 0, currentItem);
194
+
195
+ set(draftState, path, newRelations);
196
+
197
+ break;
198
+ }
184
199
  /**
185
200
  * This action will be called when you open your entry (first load)
186
201
  * but also every time you press publish.
@@ -192,6 +207,7 @@ const reducer = (state, action) =>
192
207
  componentPaths = [],
193
208
  repeatableComponentPaths = [],
194
209
  dynamicZonePaths = [],
210
+ setModifiedDataOnly,
195
211
  } = action;
196
212
 
197
213
  /**
@@ -243,7 +259,10 @@ const reducer = (state, action) =>
243
259
  return acc;
244
260
  }, data);
245
261
 
246
- draftState.initialData = mergeDataWithPreparedRelations;
262
+ if (!setModifiedDataOnly) {
263
+ draftState.initialData = mergeDataWithPreparedRelations;
264
+ }
265
+
247
266
  draftState.modifiedData = mergeDataWithPreparedRelations;
248
267
 
249
268
  draftState.formErrors = {};
@@ -252,25 +271,6 @@ const reducer = (state, action) =>
252
271
  draftState.shouldCheckErrors = false;
253
272
  break;
254
273
  }
255
- case 'MOVE_COMPONENT_FIELD': {
256
- const currentValue = get(state, ['modifiedData', ...action.pathToComponent]);
257
- const valueToInsert = get(state, [
258
- 'modifiedData',
259
- ...action.pathToComponent,
260
- action.dragIndex,
261
- ]);
262
-
263
- const updatedValue = moveFields(
264
- currentValue,
265
- action.dragIndex,
266
- action.hoverIndex,
267
- valueToInsert
268
- );
269
-
270
- set(draftState, ['modifiedData', ...action.pathToComponent], updatedValue);
271
-
272
- break;
273
- }
274
274
  case 'MOVE_COMPONENT_UP':
275
275
  case 'MOVE_COMPONENT_DOWN': {
276
276
  const { currentIndex, dynamicZoneName, shouldCheckErrors } = action;
@@ -84,14 +84,28 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
84
84
  */
85
85
  let actualOldValue = oldValue ?? [];
86
86
 
87
+ const valuesWithPositions = value.map((relation, index, allRelations) => {
88
+ const nextRelation = allRelations[index + 1];
89
+
90
+ if (nextRelation) {
91
+ return { ...relation, position: { before: nextRelation.id } };
92
+ }
93
+
94
+ return { ...relation, position: { end: true } };
95
+ });
96
+
87
97
  /**
88
98
  * Instead of the full relation object, we only want to send its ID
89
99
  * connectedRelations are the items that are in the browserState
90
100
  * array but not in the serverState
91
101
  */
92
- const connectedRelations = value.reduce((acc, relation) => {
93
- if (!actualOldValue.find((oldRelation) => oldRelation.id === relation.id)) {
94
- return [...acc, { id: relation.id }];
102
+ const connectedRelations = valuesWithPositions.reduce((acc, relation, currentIndex) => {
103
+ const indexOfRelationOnServer = actualOldValue.findIndex(
104
+ (oldRelation) => oldRelation.id === relation.id
105
+ );
106
+
107
+ if (indexOfRelationOnServer === -1 || indexOfRelationOnServer !== currentIndex) {
108
+ return [...acc, { id: relation.id, position: relation.position }];
95
109
  }
96
110
 
97
111
  return acc;
@@ -102,7 +116,7 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
102
116
  * are no longer in the browserState
103
117
  */
104
118
  const disconnectedRelations = actualOldValue.reduce((acc, relation) => {
105
- if (!value.find((newRelation) => newRelation.id === relation.id)) {
119
+ if (!valuesWithPositions.find((newRelation) => newRelation.id === relation.id)) {
106
120
  return [...acc, { id: relation.id }];
107
121
  }
108
122
 
@@ -111,7 +125,12 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
111
125
 
112
126
  cleanedData = {
113
127
  disconnect: disconnectedRelations,
114
- connect: connectedRelations,
128
+ /**
129
+ * Reverse the array because the API sequentially goes through the list
130
+ * so in an instance where you add two to the end it would fail because index0
131
+ * would want to attach itself to index1 which doesn't exist yet.
132
+ */
133
+ connect: connectedRelations.reverse(),
115
134
  };
116
135
 
117
136
  break;
@@ -55,8 +55,15 @@ const recursivelyFindPathsBasedOnConditionSetup = (components, predicate = () =>
55
55
  *
56
56
  * NOTE: we don't need to know the path to the `array` because it's about data shape not about the actual data
57
57
  */
58
- }).map((path) => path.split(`${componentName}.`)[1]);
58
+ }).map((path) => {
59
+ return path.split(`${componentName}.`)[1];
60
+ });
59
61
  })
62
+ /**
63
+ * We filter because this will give you `dynamiczone.undefined` because the dynamic_zone component
64
+ * is not required to be returned in this circumstance.
65
+ */
66
+ .filter((path) => Boolean(path))
60
67
  .map((path) => `${key}.${path}`);
61
68
 
62
69
  acc = [...acc, attributesInDynamicComponents];
@@ -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, useCustomFields } from '@strapi/helper-plugin';
8
+ import { GenericInput, NotAllowedInput, useLibrary } from '@strapi/helper-plugin';
9
9
  import { useContentTypeLayout } from '../../hooks';
10
10
  import { getFieldName } from '../../utils';
11
11
  import Wysiwyg from '../Wysiwyg';
@@ -37,11 +37,11 @@ function Inputs({
37
37
  queryInfos,
38
38
  value,
39
39
  size,
40
+ customFieldInputs,
40
41
  }) {
41
42
  const { fields } = useLibrary();
42
43
  const { formatMessage } = useIntl();
43
44
  const { contentType: currentContentTypeLayout } = useContentTypeLayout();
44
- const customFieldsRegistry = useCustomFields();
45
45
 
46
46
  const disabled = useMemo(() => !get(metadatas, 'editable', true), [metadatas]);
47
47
  const { type, customField: customFieldUid } = fieldSchema;
@@ -194,19 +194,6 @@ function Inputs({
194
194
  return minutes % metadatas.step === 0 ? metadatas.step : step;
195
195
  }, [inputType, inputValue, metadatas.step, step]);
196
196
 
197
- // Memoize the component to avoid remounting it and losing state
198
- const CustomFieldInput = useMemo(() => {
199
- if (customFieldUid) {
200
- const customField = customFieldsRegistry.get(customFieldUid);
201
- const CustomFieldInput = React.lazy(customField.components.Input);
202
-
203
- return CustomFieldInput;
204
- }
205
-
206
- // Not a custom field, component won't be used
207
- return null;
208
- }, [customFieldUid, customFieldsRegistry]);
209
-
210
197
  if (visible === false) {
211
198
  return null;
212
199
  }
@@ -268,12 +255,9 @@ function Inputs({
268
255
  media: fields.media,
269
256
  wysiwyg: Wysiwyg,
270
257
  ...fields,
258
+ ...customFieldInputs,
271
259
  };
272
260
 
273
- if (customFieldUid) {
274
- customInputs[customFieldUid] = CustomFieldInput;
275
- }
276
-
277
261
  return (
278
262
  <GenericInput
279
263
  attribute={fieldSchema}
@@ -309,6 +293,7 @@ Inputs.defaultProps = {
309
293
  size: undefined,
310
294
  value: null,
311
295
  queryInfos: {},
296
+ customFieldInputs: {},
312
297
  };
313
298
 
314
299
  Inputs.propTypes = {
@@ -330,6 +315,7 @@ Inputs.propTypes = {
330
315
  defaultParams: PropTypes.object,
331
316
  endPoint: PropTypes.string,
332
317
  }),
318
+ customFieldInputs: PropTypes.object,
333
319
  };
334
320
 
335
321
  const Memoized = memo(Inputs, isEqual);
@@ -9,6 +9,7 @@ import { Stack } from '@strapi/design-system/Stack';
9
9
  import { useContentTypeLayout } from '../../hooks';
10
10
  import FieldComponent from '../FieldComponent';
11
11
  import Inputs from '../Inputs';
12
+ import useLazyComponents from '../../hooks/useLazyComponents';
12
13
 
13
14
  const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, name }) => {
14
15
  const { getComponentLayout } = useContentTypeLayout();
@@ -18,6 +19,8 @@ const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, nam
18
19
  );
19
20
  const fields = componentLayoutData.layouts.edit;
20
21
 
22
+ const { lazyComponentStore } = useLazyComponents();
23
+
21
24
  return (
22
25
  <Box
23
26
  background={isFromDynamicZone ? '' : 'neutral100'}
@@ -67,6 +70,7 @@ const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, nam
67
70
  metadatas={metadatas}
68
71
  queryInfos={queryInfos}
69
72
  size={size}
73
+ customFieldInputs={lazyComponentStore}
70
74
  />
71
75
  </GridItem>
72
76
  );