@strapi/admin 4.6.0-alpha.1 → 4.6.0-beta.1

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 (248) hide show
  1. package/admin/src/components/AuthenticatedApp/index.js +13 -2
  2. package/admin/src/components/GlobalStyle/index.js +0 -5
  3. package/admin/src/content-manager/components/ComponentIcon/ComponentIcon.js +49 -0
  4. package/admin/src/content-manager/components/ComponentIcon/index.js +1 -0
  5. package/admin/src/content-manager/components/ComponentInitializer/index.js +1 -7
  6. package/admin/src/content-manager/components/{RepeatableComponent/DragPreview.js → DragLayer/ComponentDragPreview.js} +10 -11
  7. package/admin/src/content-manager/components/DragLayer/RelationDragPreview.js +75 -0
  8. package/admin/src/content-manager/components/DragLayer/index.js +23 -7
  9. package/admin/src/content-manager/components/DynamicZone/components/ComponentCard.js +17 -31
  10. package/admin/src/content-manager/components/DynamicZone/components/ComponentCategory.js +2 -2
  11. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +129 -85
  12. package/admin/src/content-manager/components/DynamicZone/index.js +99 -24
  13. package/admin/src/content-manager/components/DynamicZone/utils/select.js +9 -5
  14. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +76 -14
  15. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +42 -25
  16. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +24 -5
  17. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +8 -1
  18. package/admin/src/content-manager/components/InputJSON/FieldWrapper.js +10 -2
  19. package/admin/src/content-manager/components/InputJSON/Label.js +2 -18
  20. package/admin/src/content-manager/components/InputJSON/index.js +7 -3
  21. package/admin/src/content-manager/components/NonRepeatableComponent/index.js +4 -0
  22. package/admin/src/content-manager/components/RelationInput/RelationInput.js +205 -74
  23. package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +136 -21
  24. package/admin/src/content-manager/components/RelationInput/components/RelationList.js +1 -2
  25. package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
  26. package/admin/src/content-manager/components/RelationInput/index.js +1 -0
  27. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +132 -10
  28. package/admin/src/content-manager/components/RepeatableComponent/components/Accordion.js +77 -0
  29. package/admin/src/content-manager/components/RepeatableComponent/components/Component.js +262 -0
  30. package/admin/src/content-manager/components/RepeatableComponent/{DraggedItem → components}/Preview.js +0 -0
  31. package/admin/src/content-manager/components/RepeatableComponent/index.js +148 -90
  32. package/admin/src/content-manager/components/RepeatableComponent/utils/getComponentErrorKeys.js +1 -1
  33. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +1 -1
  34. package/admin/src/content-manager/components/Wysiwyg/Editor.js +1 -1
  35. package/admin/src/content-manager/hooks/index.js +2 -0
  36. package/admin/src/content-manager/hooks/useDragAndDrop.js +134 -0
  37. package/admin/src/content-manager/hooks/useKeyboardDragAndDrop.js +98 -0
  38. package/admin/src/content-manager/hooks/useLazyComponents/index.js +40 -15
  39. package/admin/src/content-manager/pages/EditSettingsView/components/DynamicZoneList.js +18 -38
  40. package/admin/src/content-manager/sharedReducers/crudReducer/actions.js +5 -0
  41. package/admin/src/content-manager/sharedReducers/crudReducer/constants.js +2 -0
  42. package/admin/src/content-manager/sharedReducers/crudReducer/reducer.js +7 -0
  43. package/admin/src/content-manager/utils/ItemTypes.js +1 -1
  44. package/admin/src/content-manager/utils/composeRefs.js +28 -0
  45. package/admin/src/content-manager/utils/getMaxTempKey.js +1 -1
  46. package/admin/src/content-manager/utils/index.js +7 -0
  47. package/admin/src/core/utils/axiosInstance.js +4 -2
  48. package/admin/src/hooks/index.js +1 -0
  49. package/admin/src/hooks/useFetchClient/index.js +23 -0
  50. package/admin/src/hooks/useSettingsMenu/init.js +0 -7
  51. package/admin/src/pages/Admin/Onboarding/index.js +42 -44
  52. package/admin/src/pages/App/index.js +20 -13
  53. package/admin/src/pages/AuthPage/components/Register/index.js +1 -1
  54. package/admin/src/pages/AuthPage/components/ResetPassword/index.js +1 -1
  55. package/admin/src/pages/HomePage/SocialLinks.js +4 -4
  56. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +2 -3
  57. package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +23 -18
  58. package/admin/src/pages/SettingsPage/utils/defaultRoutes.js +0 -11
  59. package/admin/src/permissions/defaultPermissions.js +0 -4
  60. package/admin/src/translations/en.json +8 -8
  61. package/admin/src/translations/sk.json +274 -52
  62. package/admin/src/translations/tr.json +485 -5
  63. package/admin/src/utils/fetchClient.js +45 -0
  64. package/admin/src/utils/getFetchClient.js +10 -0
  65. package/admin/src/utils/index.js +1 -0
  66. package/admin/src/utils/uniqueAdminHash.js +22 -0
  67. package/build/2235.06c13219.chunk.js +106 -0
  68. package/build/2598.962797b2.chunk.js +159 -0
  69. package/build/4318.0bbd3f4b.chunk.js +30 -0
  70. package/build/4958.7c118f5e.chunk.js +276 -0
  71. package/build/5052.712419ea.chunk.js +65 -0
  72. package/build/7295.04ac49dc.chunk.js +114 -0
  73. package/build/805.ddcead70.chunk.js +138 -0
  74. package/build/{8633.8da5488a.chunk.js → 8633.59223842.chunk.js} +1 -1
  75. package/build/874.bde3ea04.chunk.js +104 -0
  76. package/build/{1233.80b05d66.chunk.js → 9159.ac968e72.chunk.js} +67 -67
  77. package/build/9707.77e475ee.chunk.js +101 -0
  78. package/build/Admin-authenticatedApp.9dd415b8.chunk.js +72 -0
  79. package/build/{Admin_homePage.54e33c2d.chunk.js → Admin_homePage.8945f71a.chunk.js} +5 -5
  80. package/build/{Admin_marketplace.8219fda6.chunk.js → Admin_marketplace.ed754a4a.chunk.js} +1 -1
  81. package/build/Admin_pluginsPage.67728975.chunk.js +6 -0
  82. package/build/{Admin_profilePage.e9fcce92.chunk.js → Admin_profilePage.60ab80bb.chunk.js} +1 -1
  83. package/build/{Admin_settingsPage.d3f48e9e.chunk.js → Admin_settingsPage.9ce40fed.chunk.js} +15 -15
  84. package/build/Upload_ConfigureTheView.7cb2a3fd.chunk.js +1 -0
  85. package/build/admin-app.d8fc7c4d.chunk.js +112 -0
  86. package/build/admin-edit-roles-page.f407538c.chunk.js +1 -0
  87. package/build/admin-edit-users.5547b126.chunk.js +10 -0
  88. package/build/{admin-users.a0748674.chunk.js → admin-users.4b6b47f8.chunk.js} +2 -2
  89. package/build/api-tokens-create-page.dd4ddfcb.chunk.js +1 -0
  90. package/build/api-tokens-edit-page.821c5a6c.chunk.js +1 -0
  91. package/build/{api-tokens-list-page.700e575f.chunk.js → api-tokens-list-page.50519ed7.chunk.js} +1 -1
  92. package/build/content-manager.f2214e32.chunk.js +1166 -0
  93. package/build/content-type-builder-list-view.4aea46fa.chunk.js +198 -0
  94. package/build/content-type-builder-translation-de-json.a52482c7.chunk.js +1 -0
  95. package/build/content-type-builder-translation-dk-json.a8616510.chunk.js +1 -0
  96. package/build/content-type-builder-translation-en-json.1d9a3c14.chunk.js +1 -0
  97. package/build/content-type-builder-translation-es-json.c3ea46fb.chunk.js +1 -0
  98. package/build/content-type-builder-translation-ko-json.3fb7ddc8.chunk.js +1 -0
  99. package/build/content-type-builder-translation-pl-json.9b2993b2.chunk.js +1 -0
  100. package/build/content-type-builder-translation-pt-BR-json.6d255441.chunk.js +1 -0
  101. package/build/content-type-builder-translation-sv-json.c608b9ca.chunk.js +1 -0
  102. package/build/content-type-builder-translation-tr-json.949e22eb.chunk.js +1 -0
  103. package/build/content-type-builder-translation-zh-json.b79513e4.chunk.js +1 -0
  104. package/build/content-type-builder.8a9a77f9.chunk.js +127 -0
  105. package/build/email-settings-page.c6e62f6b.chunk.js +15 -0
  106. package/build/email-translation-tr-json.8aa034bb.chunk.js +1 -0
  107. package/build/en-json.1abdade9.chunk.js +1 -0
  108. package/build/{i18n-settings-page.195d42fe.chunk.js → i18n-settings-page.ee572037.chunk.js} +1 -1
  109. package/build/i18n-translation-tr-json.34ca9d61.chunk.js +1 -0
  110. package/build/index.html +1 -1
  111. package/build/main.91f6e21e.js +4099 -0
  112. package/build/runtime~main.447b0382.js +2 -0
  113. package/build/sk-json.2af48064.chunk.js +1 -0
  114. package/build/sso-settings-page.91924df1.chunk.js +41 -0
  115. package/build/tr-json.eac8bd79.chunk.js +1 -0
  116. package/build/upload-settings.326cd9fd.chunk.js +89 -0
  117. package/build/upload-translation-en-json.32cf9aff.chunk.js +1 -0
  118. package/build/upload-translation-sk-json.fe86c53b.chunk.js +1 -0
  119. package/build/upload-translation-tr-json.b173223a.chunk.js +1 -0
  120. package/build/upload.2977cb13.chunk.js +38 -0
  121. package/build/users-advanced-settings-page.0c0b8230.chunk.js +13 -0
  122. package/build/users-email-settings-page.18d4a475.chunk.js +28 -0
  123. package/build/{users-permissions-translation-dk-json.fe39c74b.chunk.js → users-permissions-translation-dk-json.bad0b786.chunk.js} +1 -1
  124. package/build/{users-permissions-translation-en-json.765abf48.chunk.js → users-permissions-translation-en-json.aeab388a.chunk.js} +1 -1
  125. package/build/{users-permissions-translation-es-json.1bb9cde2.chunk.js → users-permissions-translation-es-json.152a923f.chunk.js} +1 -1
  126. package/build/{users-permissions-translation-ko-json.3be77775.chunk.js → users-permissions-translation-ko-json.6bd0ae22.chunk.js} +1 -1
  127. package/build/{users-permissions-translation-pl-json.1dbdd4a1.chunk.js → users-permissions-translation-pl-json.c6a02992.chunk.js} +1 -1
  128. package/build/{users-permissions-translation-sv-json.d5d11648.chunk.js → users-permissions-translation-sv-json.370d6eee.chunk.js} +1 -1
  129. package/build/users-permissions-translation-tr-json.9bebc250.chunk.js +1 -0
  130. package/build/{users-permissions-translation-zh-json.92f406f9.chunk.js → users-permissions-translation-zh-json.1fea833f.chunk.js} +1 -1
  131. package/build/users-providers-settings-page.25dd858e.chunk.js +1 -0
  132. package/build/users-roles-settings-page.8482a999.chunk.js +30 -0
  133. package/build/{webhook-edit-page.14ad1e6e.chunk.js → webhook-edit-page.dcc3d145.chunk.js} +4 -4
  134. package/build/{webhook-list-page.b87821f2.chunk.js → webhook-list-page.894e6959.chunk.js} +1 -1
  135. package/ee/server/services/passport/provider-registry.js +1 -1
  136. package/package.json +16 -22
  137. package/server/controllers/admin.js +2 -0
  138. package/server/routes/admin.js +1 -1
  139. package/server/services/metrics.js +5 -2
  140. package/server/services/role.js +1 -0
  141. package/utils/get-plugins-path.js +17 -3
  142. package/webpack.alias.js +0 -2
  143. package/admin/src/content-manager/components/BackHeader/index.js +0 -8
  144. package/admin/src/content-manager/components/Block/components.js +0 -28
  145. package/admin/src/content-manager/components/Block/index.js +0 -43
  146. package/admin/src/content-manager/components/Container/index.js +0 -7
  147. package/admin/src/content-manager/components/CustomInputCheckbox/components.js +0 -77
  148. package/admin/src/content-manager/components/CustomInputCheckbox/index.js +0 -53
  149. package/admin/src/content-manager/components/DynamicComponentCard/Wrapper.js +0 -63
  150. package/admin/src/content-manager/components/FilterOptionsCTA/index.js +0 -14
  151. package/admin/src/content-manager/components/FormTitle/index.js +0 -22
  152. package/admin/src/content-manager/components/FormWrapper/index.js +0 -20
  153. package/admin/src/content-manager/components/InputJSON/FieldError.js +0 -38
  154. package/admin/src/content-manager/components/LayoutTitle/index.js +0 -19
  155. package/admin/src/content-manager/components/PlusButton/index.js +0 -52
  156. package/admin/src/content-manager/components/PreviewCarret/components.js +0 -27
  157. package/admin/src/content-manager/components/PreviewCarret/index.js +0 -22
  158. package/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js +0 -122
  159. package/admin/src/content-manager/components/RepeatableComponent/AddFieldButton.js +0 -58
  160. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js +0 -72
  161. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/IconButtonCustoms.js +0 -32
  162. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +0 -322
  163. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/connect.js +0 -11
  164. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/index.js +0 -2
  165. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js +0 -30
  166. package/admin/src/content-manager/components/RepeatableComponent/utils/connect.js +0 -11
  167. package/admin/src/content-manager/components/RepeatableComponent/utils/select.js +0 -12
  168. package/admin/src/content-manager/components/SectionTitle/Title.js +0 -11
  169. package/admin/src/content-manager/components/SectionTitle/index.js +0 -26
  170. package/admin/src/content-manager/hooks/__test__/usePrev.test.js +0 -26
  171. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/DynamicTable/TableRows/CellValue.js +0 -19
  172. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/DynamicTable/TableRows/index.js +0 -65
  173. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/ModalDialog/ActionItem.js +0 -25
  174. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/ModalDialog/index.js +0 -76
  175. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/hooks/useFormatTimeStamp.js +0 -24
  176. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/index.js +0 -84
  177. package/admin/src/pages/SettingsPage/pages/AuditLogs/ListView/utils/tableHeaders.js +0 -37
  178. package/admin/src/pages/SettingsPage/pages/AuditLogs/ProtectedListPage/index.js +0 -12
  179. package/build/1551f4f60c37af51121f.woff2 +0 -0
  180. package/build/1920.74a262e7.chunk.js +0 -245
  181. package/build/1e59d2330b4c6deb84b3.ttf +0 -0
  182. package/build/20fd1704ea223900efa9.woff2 +0 -0
  183. package/build/2285773e6b4b172f07d9.woff +0 -0
  184. package/build/23f19bb08961f37aaf69.eot +0 -0
  185. package/build/2438.61291207.chunk.js +0 -2183
  186. package/build/2517.9b4940f3.chunk.js +0 -117
  187. package/build/2f517e09eb2ca6650ff5.svg +0 -3717
  188. package/build/4306.f03c2b46.chunk.js +0 -98
  189. package/build/4318.7931eee7.chunk.js +0 -30
  190. package/build/4689f52cc96215721344.svg +0 -801
  191. package/build/491974d108fe4002b2aa.ttf +0 -0
  192. package/build/4986.3820d11d.chunk.js +0 -145
  193. package/build/504.9aeff724.chunk.js +0 -758
  194. package/build/527940b104eb2ea366c8.ttf +0 -0
  195. package/build/77206a6bb316fa0aded5.eot +0 -0
  196. package/build/7a3337626410ca2f4071.woff2 +0 -0
  197. package/build/7a8b4f130182d19a2d7c.svg +0 -5034
  198. package/build/805.e991a370.chunk.js +0 -138
  199. package/build/8b43027f47b20503057d.eot +0 -0
  200. package/build/9707.a0cc4ad8.chunk.js +0 -70
  201. package/build/9bbb245e67a133f6e486.eot +0 -0
  202. package/build/Admin-authenticatedApp.ac85652e.chunk.js +0 -80
  203. package/build/Admin_pluginsPage.3c872de7.chunk.js +0 -6
  204. package/build/admin-app.77179e07.chunk.js +0 -112
  205. package/build/admin-audit-logs.334ee871.chunk.js +0 -1
  206. package/build/admin-edit-roles-page.23f15909.chunk.js +0 -1
  207. package/build/admin-edit-users.283b49ed.chunk.js +0 -10
  208. package/build/api-tokens-create-page.93dd0689.chunk.js +0 -1
  209. package/build/api-tokens-edit-page.b0adac81.chunk.js +0 -1
  210. package/build/bb58e57c48a3e911f15f.woff +0 -0
  211. package/build/be9ee23c0c6390141475.ttf +0 -0
  212. package/build/c1e38fd9e0e74ba58f7a.svg +0 -2671
  213. package/build/content-manager.01e04e11.chunk.js +0 -1200
  214. package/build/content-type-builder-list-view.4412efc3.chunk.js +0 -201
  215. package/build/content-type-builder-translation-de-json.0d7696b9.chunk.js +0 -1
  216. package/build/content-type-builder-translation-dk-json.4729f055.chunk.js +0 -1
  217. package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +0 -1
  218. package/build/content-type-builder-translation-es-json.333cf47f.chunk.js +0 -1
  219. package/build/content-type-builder-translation-ko-json.51201b12.chunk.js +0 -1
  220. package/build/content-type-builder-translation-pl-json.4a42349b.chunk.js +0 -1
  221. package/build/content-type-builder-translation-pt-BR-json.6fe3b8d1.chunk.js +0 -1
  222. package/build/content-type-builder-translation-sv-json.6deff030.chunk.js +0 -1
  223. package/build/content-type-builder-translation-tr-json.2e52bc60.chunk.js +0 -1
  224. package/build/content-type-builder-translation-zh-json.3b0afd31.chunk.js +0 -1
  225. package/build/content-type-builder.aa4ec633.chunk.js +0 -145
  226. package/build/d878b0a6a1144760244f.woff2 +0 -0
  227. package/build/eeccf4f66002c6f2ba24.woff +0 -0
  228. package/build/email-settings-page.d44a57cb.chunk.js +0 -15
  229. package/build/email-translation-tr-json.87f2feb3.chunk.js +0 -1
  230. package/build/en-json.57917cb1.chunk.js +0 -1
  231. package/build/f691f37e57f04c152e23.woff +0 -0
  232. package/build/fontawesome-css-all.15068c6e.chunk.js +0 -4618
  233. package/build/fontawesome-css.418f40da.chunk.js +0 -6
  234. package/build/fontawesome-js.252cc5f3.chunk.js +0 -7
  235. package/build/main.f31112a5.js +0 -2034
  236. package/build/runtime~main.81f05721.js +0 -2
  237. package/build/sk-json.7ba4b330.chunk.js +0 -1
  238. package/build/sso-settings-page.9f091262.chunk.js +0 -1
  239. package/build/tr-json.9c44ea0c.chunk.js +0 -1
  240. package/build/upload-settings.450cab1a.chunk.js +0 -18
  241. package/build/upload-translation-en-json.86da7b0a.chunk.js +0 -1
  242. package/build/upload-translation-sk-json.b03d4904.chunk.js +0 -1
  243. package/build/upload.a73936d9.chunk.js +0 -64
  244. package/build/users-advanced-settings-page.dc23bc56.chunk.js +0 -13
  245. package/build/users-email-settings-page.6541d372.chunk.js +0 -28
  246. package/build/users-permissions-translation-tr-json.cdc49a3c.chunk.js +0 -1
  247. package/build/users-providers-settings-page.e11a2f64.chunk.js +0 -33
  248. package/build/users-roles-settings-page.445e5e16.chunk.js +0 -30
@@ -1,24 +1,22 @@
1
- import React, { memo, useCallback, useMemo, useState } from 'react';
2
1
  /* eslint-disable import/no-cycle */
3
- import { useDrop } from 'react-dnd';
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
- import { useNotification } from '@strapi/helper-plugin';
9
- import { Box } from '@strapi/design-system/Box';
10
- import { Flex } from '@strapi/design-system/Flex';
11
- import { TextButton } from '@strapi/design-system/TextButton';
12
- import Plus from '@strapi/icons/Plus';
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
- import ItemTypes from '../../utils/ItemTypes';
14
+
16
15
  import ComponentInitializer from '../ComponentInitializer';
17
- import connect from './utils/connect';
18
- import select from './utils/select';
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,32 +39,31 @@ 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 [isDraggingSibling, setIsDraggingSibling] = useState(false);
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),
54
51
  [componentUid, getComponentLayout]
55
52
  );
56
53
 
57
- const nextTempKey = useMemo(() => {
58
- return getMaxTempKey(componentValue || []) + 1;
59
- }, [componentValue]);
54
+ const nextTempKey = useMemo(() => getMaxTempKey(componentValue || []) + 1, [componentValue]);
60
55
 
61
56
  const componentErrorKeys = getComponentErrorKeys(name, formErrors);
62
57
 
63
- const toggleCollapses = () => {
64
- setCollapseToOpen('');
65
- };
66
-
67
58
  const missingComponentsValue = min - componentValueLength;
68
59
 
69
60
  const hasMinError = get(formErrors, name, { id: '' }).id.includes('min');
70
61
 
71
- const handleClick = useCallback(() => {
62
+ const toggleCollapses = () => {
63
+ setCollapseToOpen('');
64
+ };
65
+
66
+ const handleClick = () => {
72
67
  if (!isReadOnly) {
73
68
  if (componentValueLength < max) {
74
69
  const shouldCheckErrors = hasMinError;
@@ -83,18 +78,89 @@ const RepeatableComponent = ({
83
78
  });
84
79
  }
85
80
  }
86
- }, [
87
- components,
88
- addRepeatableComponentToField,
89
- componentLayoutData,
90
- componentValueLength,
91
- hasMinError,
92
- isReadOnly,
93
- max,
94
- name,
95
- nextTempKey,
96
- toggleNotification,
97
- ]);
81
+ };
82
+
83
+ const handleMoveComponentField = (newIndex, currentIndex) => {
84
+ setLiveText(
85
+ formatMessage(
86
+ {
87
+ id: getTrad('dnd.reorder'),
88
+ defaultMessage: '{item}, moved. New position in list: {position}.',
89
+ },
90
+ {
91
+ item: `${name}.${currentIndex}`,
92
+ position: getItemPos(newIndex),
93
+ }
94
+ )
95
+ );
96
+
97
+ moveComponentField({
98
+ name,
99
+ newIndex,
100
+ currentIndex,
101
+ });
102
+ };
103
+
104
+ const mainField = get(componentLayoutData, ['settings', 'mainField'], 'id');
105
+
106
+ const handleToggle = (key) => () => {
107
+ if (collapseToOpen === key) {
108
+ setCollapseToOpen('');
109
+ } else {
110
+ setCollapseToOpen(key);
111
+ }
112
+ };
113
+
114
+ /**
115
+ *
116
+ * @param {number} index
117
+ * @returns {string}
118
+ */
119
+ const getItemPos = (index) => `${index + 1} of ${componentValueLength}`;
120
+
121
+ const handleCancel = (index) => {
122
+ setLiveText(
123
+ formatMessage(
124
+ {
125
+ id: getTrad('dnd.cancel-item'),
126
+ defaultMessage: '{item}, dropped. Re-order cancelled.',
127
+ },
128
+ {
129
+ item: `${name}.${index}`,
130
+ }
131
+ )
132
+ );
133
+ };
134
+
135
+ const handleGrabItem = (index) => {
136
+ setLiveText(
137
+ formatMessage(
138
+ {
139
+ id: getTrad('dnd.grab-item'),
140
+ defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`,
141
+ },
142
+ {
143
+ item: `${name}.${index}`,
144
+ position: getItemPos(index),
145
+ }
146
+ )
147
+ );
148
+ };
149
+
150
+ const handleDropItem = (index) => {
151
+ setLiveText(
152
+ formatMessage(
153
+ {
154
+ id: getTrad('dnd.drop-item'),
155
+ defaultMessage: `{item}, dropped. Final position in list: {position}.`,
156
+ },
157
+ {
158
+ item: `${name}.${index}`,
159
+ position: getItemPos(index),
160
+ }
161
+ )
162
+ );
163
+ };
98
164
 
99
165
  let errorMessage = formErrors[name];
100
166
 
@@ -105,6 +171,11 @@ const RepeatableComponent = ({
105
171
  'There {number, plural, =0 {are # missing components} one {is # missing component} other {are # missing components}}',
106
172
  values: { number: missingComponentsValue },
107
173
  };
174
+ } else if (componentErrorKeys.some((error) => error.split('.').length > 1) && !hasMinError) {
175
+ errorMessage = {
176
+ id: getTrad('components.RepeatableComponent.error-message'),
177
+ defaultMessage: 'The component(s) contain error(s)',
178
+ };
108
179
  }
109
180
 
110
181
  if (componentValueLength === 0) {
@@ -113,22 +184,44 @@ const RepeatableComponent = ({
113
184
  );
114
185
  }
115
186
 
116
- const doesRepComponentHasChildError = componentErrorKeys.some(
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
- }
187
+ const ariaDescriptionId = `${name}-item-instructions`;
126
188
 
127
189
  return (
128
- <Box hasRadius ref={drop}>
129
- <AccordionGroupCustom
130
- error={errorMessage}
131
- footer={
190
+ <Box hasRadius>
191
+ <VisuallyHidden id={ariaDescriptionId}>
192
+ {formatMessage({
193
+ id: getTrad('dnd.instructions'),
194
+ defaultMessage: `Press spacebar to grab and re-order`,
195
+ })}
196
+ </VisuallyHidden>
197
+ <VisuallyHidden aria-live="assertive">{liveText}</VisuallyHidden>
198
+ <Accordion.Group error={errorMessage} ariaDescribedBy={ariaDescriptionId}>
199
+ <Accordion.Content aria-describedby={ariaDescriptionId}>
200
+ {componentValue.map((data, index) => {
201
+ const key = data.__temp_key__;
202
+ const componentFieldName = `${name}.${index}`;
203
+
204
+ return (
205
+ <Component
206
+ componentFieldName={componentFieldName}
207
+ componentUid={componentUid}
208
+ fields={componentLayoutData.layouts.edit}
209
+ key={key}
210
+ index={index}
211
+ isOpen={collapseToOpen === key}
212
+ isReadOnly={isReadOnly}
213
+ mainField={mainField}
214
+ moveComponentField={handleMoveComponentField}
215
+ onClickToggle={handleToggle(key)}
216
+ toggleCollapses={toggleCollapses}
217
+ onCancel={handleCancel}
218
+ onDropItem={handleDropItem}
219
+ onGrabItem={handleGrabItem}
220
+ />
221
+ );
222
+ })}
223
+ </Accordion.Content>
224
+ <Accordion.Footer>
132
225
  <Flex justifyContent="center" height="48px" background="neutral0">
133
226
  <TextButtonCustom disabled={isReadOnly} onClick={handleClick} startIcon={<Plus />}>
134
227
  {formatMessage({
@@ -137,39 +230,8 @@ const RepeatableComponent = ({
137
230
  })}
138
231
  </TextButtonCustom>
139
232
  </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>
233
+ </Accordion.Footer>
234
+ </Accordion.Group>
173
235
  </Box>
174
236
  );
175
237
  };
@@ -177,25 +239,21 @@ const RepeatableComponent = ({
177
239
  RepeatableComponent.defaultProps = {
178
240
  componentValue: null,
179
241
  componentValueLength: 0,
180
- formErrors: {},
242
+ isReadOnly: false,
181
243
  max: Infinity,
182
244
  min: 0,
183
245
  };
184
246
 
185
247
  RepeatableComponent.propTypes = {
186
- addRepeatableComponentToField: PropTypes.func.isRequired,
187
248
  componentUid: PropTypes.string.isRequired,
188
249
  componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
189
250
  componentValueLength: PropTypes.number,
190
- formErrors: PropTypes.object,
191
- isReadOnly: PropTypes.bool.isRequired,
251
+ isReadOnly: PropTypes.bool,
192
252
  max: PropTypes.number,
193
253
  min: PropTypes.number,
194
254
  name: PropTypes.string.isRequired,
195
255
  };
196
256
 
197
- const Memoized = memo(RepeatableComponent);
198
-
199
- export default connect(Memoized, select);
257
+ export default memo(RepeatableComponent);
200
258
 
201
259
  export { RepeatableComponent };
@@ -1,4 +1,4 @@
1
- export default function getComponentErrorKeys(name, formErrors) {
1
+ export default function getComponentErrorKeys(name, formErrors = {}) {
2
2
  return Object.keys(formErrors)
3
3
  .filter((errorKey) => errorKey.startsWith(name))
4
4
  .map((errorKey) =>
@@ -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.state.focused) {
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,134 @@
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
+ * dropSensitivity?: 'regular' | 'immediate'
16
+ * } & import('./useKeyboardDragAndDrop').UseKeyboardDragAndDropCallbacks}
17
+ */
18
+
19
+ /**
20
+ * @typedef UseDragAndDropReturn
21
+ *
22
+ * @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]}
23
+ */
24
+
25
+ /**
26
+ * A utility hook abstracting the general drag and drop hooks from react-dnd.
27
+ * Centralising the same behaviours and by default offering keyboard support.
28
+ *
29
+ * @type {(active: boolean, options: UseDragAndDropOptions) => UseDragAndDropReturn}
30
+ */
31
+ export const useDragAndDrop = (
32
+ active,
33
+ {
34
+ type = 'STRAPI_DND',
35
+ index,
36
+ item = {},
37
+ onStart,
38
+ onEnd,
39
+ onGrabItem,
40
+ onDropItem,
41
+ onCancel,
42
+ onMoveItem,
43
+ dropSensitivity = 'regular',
44
+ }
45
+ ) => {
46
+ const objectRef = useRef(null);
47
+
48
+ const [{ handlerId }, dropRef] = useDrop({
49
+ accept: type,
50
+ collect(monitor) {
51
+ return {
52
+ handlerId: monitor.getHandlerId(),
53
+ };
54
+ },
55
+ hover(item, monitor) {
56
+ if (!objectRef.current) {
57
+ return;
58
+ }
59
+ const dragIndex = item.index;
60
+ const newInd = index;
61
+
62
+ // Don't replace items with themselves
63
+ if (dragIndex === newInd) {
64
+ return;
65
+ }
66
+
67
+ if (dropSensitivity === 'regular') {
68
+ const hoverBoundingRect = objectRef.current.getBoundingClientRect();
69
+ const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
70
+ const clientOffset = monitor.getClientOffset();
71
+ const hoverClientY = clientOffset.y - hoverBoundingRect.top;
72
+
73
+ // Dragging downwards
74
+ if (dragIndex < newInd && hoverClientY < hoverMiddleY) {
75
+ return;
76
+ }
77
+
78
+ // Dragging upwards
79
+ if (dragIndex > newInd && hoverClientY > hoverMiddleY) {
80
+ return;
81
+ }
82
+ }
83
+
84
+ // Time to actually perform the action
85
+ onMoveItem(newInd, dragIndex);
86
+ item.index = newInd;
87
+ },
88
+ });
89
+
90
+ const [{ isDragging }, dragRef, dragPreviewRef] = useDrag({
91
+ type,
92
+ item() {
93
+ if (onStart) {
94
+ onStart();
95
+ }
96
+
97
+ /**
98
+ * This will be attached and it helps define the preview sizes
99
+ * when a component is flexy e.g. Relations
100
+ */
101
+ const { width } = objectRef.current?.getBoundingClientRect() ?? {};
102
+
103
+ return { index, width, ...item };
104
+ },
105
+ end() {
106
+ if (onEnd) {
107
+ onEnd();
108
+ }
109
+ },
110
+ canDrag: active,
111
+ /**
112
+ * This is for useful when the item is in a virtualized list.
113
+ * However, if we don't have an ID then we want the libraries
114
+ * defaults to take care of this.
115
+ */
116
+ isDragging: item.id
117
+ ? (monitor) => {
118
+ return item.id === monitor.getItem().id;
119
+ }
120
+ : undefined,
121
+ collect: (monitor) => ({
122
+ isDragging: monitor.isDragging(),
123
+ }),
124
+ });
125
+
126
+ const handleKeyDown = useKeyboardDragAndDrop(index, {
127
+ onGrabItem,
128
+ onDropItem,
129
+ onCancel,
130
+ onMoveItem,
131
+ });
132
+
133
+ return [{ handlerId, isDragging, handleKeyDown }, objectRef, dropRef, dragRef, dragPreviewRef];
134
+ };
@@ -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
+ };
@@ -1,44 +1,69 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useCallback, useEffect, useState } from 'react';
2
2
  import { useCustomFields } from '@strapi/helper-plugin';
3
3
 
4
+ const componentStore = new Map();
5
+
4
6
  /**
5
7
  * @description
6
8
  * A hook to lazy load custom field components
7
9
  * @param {Array.<string>} componentUids - The uids to look up components
8
10
  * @returns object
9
11
  */
10
- const useLazyComponents = (componentUids) => {
11
- const [lazyComponentStore, setLazyComponentStore] = useState({});
12
- const [loading, setLoading] = useState(true);
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
+ });
13
21
  const customFieldsRegistry = useCustomFields();
14
22
 
15
23
  useEffect(() => {
24
+ const setStore = (store) => {
25
+ setLazyComponentStore(store);
26
+ setLoading(false);
27
+ };
28
+
16
29
  const lazyLoadComponents = async (uids, components) => {
17
30
  const modules = await Promise.all(components);
18
31
 
19
32
  uids.forEach((uid, index) => {
20
- if (!Object.keys(lazyComponentStore).includes(uid)) {
21
- setLazyComponentStore({ ...lazyComponentStore, [uid]: modules[index].default });
22
- }
33
+ componentStore.set(uid, modules[index].default);
23
34
  });
35
+
36
+ setStore(Object.fromEntries(componentStore));
24
37
  };
25
38
 
26
- if (componentUids.length) {
27
- const componentPromises = componentUids.map((uid) => {
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) => {
28
46
  const customField = customFieldsRegistry.get(uid);
29
47
 
30
48
  return customField.components.Input();
31
49
  });
32
50
 
33
- lazyLoadComponents(componentUids, componentPromises);
51
+ if (componentPromises.length > 0) {
52
+ lazyLoadComponents(newUids, componentPromises);
53
+ }
34
54
  }
55
+ }, [componentUids, customFieldsRegistry, loading]);
35
56
 
36
- if (componentUids.length === Object.keys(lazyComponentStore).length) {
37
- setLoading(false);
38
- }
39
- }, [componentUids, customFieldsRegistry, loading, lazyComponentStore]);
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
+ }, []);
40
65
 
41
- return { isLazyLoading: loading, lazyComponentStore };
66
+ return { isLazyLoading: loading, lazyComponentStore, cleanup };
42
67
  };
43
68
 
44
69
  export default useLazyComponents;