@strapi/content-manager 5.61.1 → 5.62.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 (260) hide show
  1. package/dist/admin/components/ConfigurationForm/Fields.js +1 -1
  2. package/dist/admin/components/ConfigurationForm/Fields.js.map +1 -1
  3. package/dist/admin/components/ConfigurationForm/Fields.mjs +3 -3
  4. package/dist/admin/components/ConfigurationForm/Fields.mjs.map +1 -1
  5. package/dist/admin/features/I18nSharedFieldsLock.js +54 -0
  6. package/dist/admin/features/I18nSharedFieldsLock.js.map +1 -0
  7. package/dist/admin/features/I18nSharedFieldsLock.mjs +31 -0
  8. package/dist/admin/features/I18nSharedFieldsLock.mjs.map +1 -0
  9. package/dist/admin/history/components/VersionInputRenderer.js +7 -2
  10. package/dist/admin/history/components/VersionInputRenderer.js.map +1 -1
  11. package/dist/admin/history/components/VersionInputRenderer.mjs +7 -3
  12. package/dist/admin/history/components/VersionInputRenderer.mjs.map +1 -1
  13. package/dist/admin/hooks/useContentManagerInitData.js +27 -8
  14. package/dist/admin/hooks/useContentManagerInitData.js.map +1 -1
  15. package/dist/admin/hooks/useContentManagerInitData.mjs +29 -10
  16. package/dist/admin/hooks/useContentManagerInitData.mjs.map +1 -1
  17. package/dist/admin/hooks/useDocument.js +11 -13
  18. package/dist/admin/hooks/useDocument.js.map +1 -1
  19. package/dist/admin/hooks/useDocument.mjs +11 -13
  20. package/dist/admin/hooks/useDocument.mjs.map +1 -1
  21. package/dist/admin/hooks/useI18nFieldLock.js +169 -0
  22. package/dist/admin/hooks/useI18nFieldLock.js.map +1 -0
  23. package/dist/admin/hooks/useI18nFieldLock.mjs +140 -0
  24. package/dist/admin/hooks/useI18nFieldLock.mjs.map +1 -0
  25. package/dist/admin/hooks/usePersistentQueryParams.js +16 -3
  26. package/dist/admin/hooks/usePersistentQueryParams.js.map +1 -1
  27. package/dist/admin/hooks/usePersistentQueryParams.mjs +16 -3
  28. package/dist/admin/hooks/usePersistentQueryParams.mjs.map +1 -1
  29. package/dist/admin/index.js +5 -0
  30. package/dist/admin/index.js.map +1 -1
  31. package/dist/admin/index.mjs +2 -0
  32. package/dist/admin/index.mjs.map +1 -1
  33. package/dist/admin/modules/app.js +8 -1
  34. package/dist/admin/modules/app.js.map +1 -1
  35. package/dist/admin/modules/app.mjs +8 -2
  36. package/dist/admin/modules/app.mjs.map +1 -1
  37. package/dist/admin/pages/EditView/EditViewPage.js +129 -127
  38. package/dist/admin/pages/EditView/EditViewPage.js.map +1 -1
  39. package/dist/admin/pages/EditView/EditViewPage.mjs +129 -127
  40. package/dist/admin/pages/EditView/EditViewPage.mjs.map +1 -1
  41. package/dist/admin/pages/EditView/components/DocumentActions.js +11 -4
  42. package/dist/admin/pages/EditView/components/DocumentActions.js.map +1 -1
  43. package/dist/admin/pages/EditView/components/DocumentActions.mjs +12 -5
  44. package/dist/admin/pages/EditView/components/DocumentActions.mjs.map +1 -1
  45. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.js +31 -21
  46. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.js.map +1 -1
  47. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.mjs +32 -22
  48. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/EditorLayout.mjs.map +1 -1
  49. package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.js +98 -0
  50. package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.js.map +1 -0
  51. package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.mjs +96 -0
  52. package/dist/admin/pages/EditView/components/FormInputs/Component/AddComponentMenu.mjs.map +1 -0
  53. package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.js +1103 -0
  54. package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.js.map +1 -0
  55. package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.mjs +1077 -0
  56. package/dist/admin/pages/EditView/components/FormInputs/Component/ComponentCopyModal.mjs.map +1 -0
  57. package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.js +211 -0
  58. package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.js.map +1 -0
  59. package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.mjs +183 -0
  60. package/dist/admin/pages/EditView/components/FormInputs/Component/CopiedComponents.mjs.map +1 -0
  61. package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.js +84 -41
  62. package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.js.map +1 -1
  63. package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.mjs +86 -43
  64. package/dist/admin/pages/EditView/components/FormInputs/Component/Initializer.mjs.map +1 -1
  65. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.js +86 -20
  66. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.js.map +1 -1
  67. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.mjs +89 -23
  68. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.mjs.map +1 -1
  69. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.js +5 -2
  70. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.js.map +1 -1
  71. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.mjs +5 -2
  72. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.mjs.map +1 -1
  73. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js +142 -22
  74. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js.map +1 -1
  75. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs +143 -23
  76. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs.map +1 -1
  77. package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.js +402 -0
  78. package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.js.map +1 -0
  79. package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.mjs +389 -0
  80. package/dist/admin/pages/EditView/components/FormInputs/Component/utils/componentCopy.mjs.map +1 -0
  81. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js +84 -33
  82. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js.map +1 -1
  83. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs +85 -34
  84. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs.map +1 -1
  85. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.js +5 -1
  86. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.js.map +1 -1
  87. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.mjs +5 -1
  88. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.mjs.map +1 -1
  89. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js +17 -4
  90. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js.map +1 -1
  91. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs +18 -5
  92. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs.map +1 -1
  93. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js +54 -6
  94. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js.map +1 -1
  95. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs +54 -6
  96. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs.map +1 -1
  97. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js +11 -9
  98. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js.map +1 -1
  99. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs +12 -10
  100. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs.map +1 -1
  101. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +9 -4
  102. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
  103. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +9 -4
  104. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
  105. package/dist/admin/pages/EditView/components/FormLayout.js +66 -63
  106. package/dist/admin/pages/EditView/components/FormLayout.js.map +1 -1
  107. package/dist/admin/pages/EditView/components/FormLayout.mjs +66 -63
  108. package/dist/admin/pages/EditView/components/FormLayout.mjs.map +1 -1
  109. package/dist/admin/pages/EditView/components/InputRenderer.js +7 -1
  110. package/dist/admin/pages/EditView/components/InputRenderer.js.map +1 -1
  111. package/dist/admin/pages/EditView/components/InputRenderer.mjs +7 -1
  112. package/dist/admin/pages/EditView/components/InputRenderer.mjs.map +1 -1
  113. package/dist/admin/pages/EditView/utils/data.js +14 -9
  114. package/dist/admin/pages/EditView/utils/data.js.map +1 -1
  115. package/dist/admin/pages/EditView/utils/data.mjs +14 -9
  116. package/dist/admin/pages/EditView/utils/data.mjs.map +1 -1
  117. package/dist/admin/pages/EditView/utils/forms.js +34 -0
  118. package/dist/admin/pages/EditView/utils/forms.js.map +1 -1
  119. package/dist/admin/pages/EditView/utils/forms.mjs +34 -1
  120. package/dist/admin/pages/EditView/utils/forms.mjs.map +1 -1
  121. package/dist/admin/pages/ListConfiguration/components/DraggableCard.js +2 -1
  122. package/dist/admin/pages/ListConfiguration/components/DraggableCard.js.map +1 -1
  123. package/dist/admin/pages/ListConfiguration/components/DraggableCard.mjs +2 -1
  124. package/dist/admin/pages/ListConfiguration/components/DraggableCard.mjs.map +1 -1
  125. package/dist/admin/pages/ListConfiguration/components/EditFieldForm.js +7 -6
  126. package/dist/admin/pages/ListConfiguration/components/EditFieldForm.js.map +1 -1
  127. package/dist/admin/pages/ListConfiguration/components/EditFieldForm.mjs +7 -6
  128. package/dist/admin/pages/ListConfiguration/components/EditFieldForm.mjs.map +1 -1
  129. package/dist/admin/pages/ListView/ListViewPage.js +11 -2
  130. package/dist/admin/pages/ListView/ListViewPage.js.map +1 -1
  131. package/dist/admin/pages/ListView/ListViewPage.mjs +13 -4
  132. package/dist/admin/pages/ListView/ListViewPage.mjs.map +1 -1
  133. package/dist/admin/pages/ListView/components/Filters.js +15 -5
  134. package/dist/admin/pages/ListView/components/Filters.js.map +1 -1
  135. package/dist/admin/pages/ListView/components/Filters.mjs +16 -7
  136. package/dist/admin/pages/ListView/components/Filters.mjs.map +1 -1
  137. package/dist/admin/preview/components/InputPopover.js +18 -8
  138. package/dist/admin/preview/components/InputPopover.js.map +1 -1
  139. package/dist/admin/preview/components/InputPopover.mjs +19 -9
  140. package/dist/admin/preview/components/InputPopover.mjs.map +1 -1
  141. package/dist/admin/preview/pages/Preview.js +5 -16
  142. package/dist/admin/preview/pages/Preview.js.map +1 -1
  143. package/dist/admin/preview/pages/Preview.mjs +5 -16
  144. package/dist/admin/preview/pages/Preview.mjs.map +1 -1
  145. package/dist/admin/preview/utils/fieldUtils.js +2 -0
  146. package/dist/admin/preview/utils/fieldUtils.js.map +1 -1
  147. package/dist/admin/preview/utils/fieldUtils.mjs +2 -1
  148. package/dist/admin/preview/utils/fieldUtils.mjs.map +1 -1
  149. package/dist/admin/services/api.js +2 -1
  150. package/dist/admin/services/api.js.map +1 -1
  151. package/dist/admin/services/api.mjs +2 -1
  152. package/dist/admin/services/api.mjs.map +1 -1
  153. package/dist/admin/services/documents.js +21 -1
  154. package/dist/admin/services/documents.js.map +1 -1
  155. package/dist/admin/services/documents.mjs +21 -2
  156. package/dist/admin/services/documents.mjs.map +1 -1
  157. package/dist/admin/services/relations.js +47 -1
  158. package/dist/admin/services/relations.js.map +1 -1
  159. package/dist/admin/services/relations.mjs +47 -2
  160. package/dist/admin/services/relations.mjs.map +1 -1
  161. package/dist/admin/src/exports.d.ts +2 -0
  162. package/dist/admin/src/features/I18nSharedFieldsLock.d.ts +22 -0
  163. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +7 -1
  164. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  165. package/dist/admin/src/hooks/useI18nFieldLock.d.ts +61 -0
  166. package/dist/admin/src/modules/app.d.ts +4 -1
  167. package/dist/admin/src/pages/EditView/components/FormInputs/Component/AddComponentMenu.d.ts +15 -0
  168. package/dist/admin/src/pages/EditView/components/FormInputs/Component/ComponentCopyModal.d.ts +58 -0
  169. package/dist/admin/src/pages/EditView/components/FormInputs/Component/CopiedComponents.d.ts +41 -0
  170. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Initializer.d.ts +7 -3
  171. package/dist/admin/src/pages/EditView/components/FormInputs/Component/NonRepeatable.d.ts +1 -1
  172. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Repeatable.d.ts +2 -2
  173. package/dist/admin/src/pages/EditView/components/FormInputs/Component/utils/componentCopy.d.ts +115 -0
  174. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +6 -3
  175. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentPicker.d.ts +2 -1
  176. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +5 -1
  177. package/dist/admin/src/pages/EditView/utils/forms.d.ts +13 -2
  178. package/dist/admin/src/pages/ListView/components/Filters.d.ts +12 -3
  179. package/dist/admin/src/preview/services/preview.d.ts +1 -1
  180. package/dist/admin/src/preview/utils/fieldUtils.d.ts +1 -0
  181. package/dist/admin/src/services/api.d.ts +1 -1
  182. package/dist/admin/src/services/components.d.ts +2 -2
  183. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  184. package/dist/admin/src/services/documents.d.ts +25 -21
  185. package/dist/admin/src/services/homepage.d.ts +1 -1
  186. package/dist/admin/src/services/init.d.ts +1 -1
  187. package/dist/admin/src/services/relations.d.ts +5 -3
  188. package/dist/admin/src/services/uid.d.ts +3 -3
  189. package/dist/admin/translations/en.json.js +41 -0
  190. package/dist/admin/translations/en.json.js.map +1 -1
  191. package/dist/admin/translations/en.json.mjs +41 -0
  192. package/dist/admin/translations/en.json.mjs.map +1 -1
  193. package/dist/server/controllers/relations.js +4 -2
  194. package/dist/server/controllers/relations.js.map +1 -1
  195. package/dist/server/controllers/relations.mjs +4 -2
  196. package/dist/server/controllers/relations.mjs.map +1 -1
  197. package/dist/server/controllers/utils/metadata.js +3 -1
  198. package/dist/server/controllers/utils/metadata.js.map +1 -1
  199. package/dist/server/controllers/utils/metadata.mjs +3 -1
  200. package/dist/server/controllers/utils/metadata.mjs.map +1 -1
  201. package/dist/server/history/services/utils.js +3 -1
  202. package/dist/server/history/services/utils.js.map +1 -1
  203. package/dist/server/history/services/utils.mjs +3 -1
  204. package/dist/server/history/services/utils.mjs.map +1 -1
  205. package/dist/server/homepage/services/homepage-query-utils.js +14 -0
  206. package/dist/server/homepage/services/homepage-query-utils.js.map +1 -1
  207. package/dist/server/homepage/services/homepage-query-utils.mjs +14 -1
  208. package/dist/server/homepage/services/homepage-query-utils.mjs.map +1 -1
  209. package/dist/server/homepage/services/homepage.js +1 -1
  210. package/dist/server/homepage/services/homepage.js.map +1 -1
  211. package/dist/server/homepage/services/homepage.mjs +2 -2
  212. package/dist/server/homepage/services/homepage.mjs.map +1 -1
  213. package/dist/server/mcp/sanitizers/shape-relations.js +1 -3
  214. package/dist/server/mcp/sanitizers/shape-relations.js.map +1 -1
  215. package/dist/server/mcp/sanitizers/shape-relations.mjs +1 -3
  216. package/dist/server/mcp/sanitizers/shape-relations.mjs.map +1 -1
  217. package/dist/server/mcp/schemas/output-schemas.js +2 -1
  218. package/dist/server/mcp/schemas/output-schemas.js.map +1 -1
  219. package/dist/server/mcp/schemas/output-schemas.mjs +2 -1
  220. package/dist/server/mcp/schemas/output-schemas.mjs.map +1 -1
  221. package/dist/server/services/document-manager.js +3 -2
  222. package/dist/server/services/document-manager.js.map +1 -1
  223. package/dist/server/services/document-manager.mjs +3 -2
  224. package/dist/server/services/document-manager.mjs.map +1 -1
  225. package/dist/server/services/document-metadata.js +92 -4
  226. package/dist/server/services/document-metadata.js.map +1 -1
  227. package/dist/server/services/document-metadata.mjs +92 -4
  228. package/dist/server/services/document-metadata.mjs.map +1 -1
  229. package/dist/server/services/permission-checker.js +20 -8
  230. package/dist/server/services/permission-checker.js.map +1 -1
  231. package/dist/server/services/permission-checker.mjs +20 -8
  232. package/dist/server/services/permission-checker.mjs.map +1 -1
  233. package/dist/server/services/uid.js +2 -1
  234. package/dist/server/services/uid.js.map +1 -1
  235. package/dist/server/services/uid.mjs +2 -1
  236. package/dist/server/services/uid.mjs.map +1 -1
  237. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  238. package/dist/server/src/controllers/utils/metadata.d.ts +1 -0
  239. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  240. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  241. package/dist/server/src/homepage/services/homepage-query-utils.d.ts +6 -0
  242. package/dist/server/src/homepage/services/homepage-query-utils.d.ts.map +1 -1
  243. package/dist/server/src/homepage/services/homepage.d.ts.map +1 -1
  244. package/dist/server/src/index.d.ts +21 -7
  245. package/dist/server/src/index.d.ts.map +1 -1
  246. package/dist/server/src/mcp/sanitizers/shape-relations.d.ts.map +1 -1
  247. package/dist/server/src/mcp/schemas/output-schemas.d.ts.map +1 -1
  248. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  249. package/dist/server/src/services/document-metadata.d.ts +5 -1
  250. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  251. package/dist/server/src/services/index.d.ts +21 -7
  252. package/dist/server/src/services/index.d.ts.map +1 -1
  253. package/dist/server/src/services/permission-checker.d.ts +6 -5
  254. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  255. package/dist/server/src/services/uid.d.ts.map +1 -1
  256. package/dist/shared/contracts/collection-types.d.ts +7 -0
  257. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  258. package/dist/shared/contracts/single-types.d.ts +4 -0
  259. package/dist/shared/contracts/single-types.d.ts.map +1 -1
  260. package/package.json +9 -9
@@ -0,0 +1,1103 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
6
+ var designSystem = require('@strapi/design-system');
7
+ var Icons = require('@strapi/icons');
8
+ var toPath = require('lodash/toPath');
9
+ var reactIntl = require('react-intl');
10
+ var styledComponents = require('styled-components');
11
+ var collections = require('../../../../../constants/collections.js');
12
+ var useDebounce = require('../../../../../hooks/useDebounce.js');
13
+ var useDocumentContext = require('../../../../../hooks/useDocumentContext.js');
14
+ var useDocumentLayout = require('../../../../../hooks/useDocumentLayout.js');
15
+ var documents = require('../../../../../services/documents.js');
16
+ var relations = require('../../../../../services/relations.js');
17
+ var translations = require('../../../../../utils/translations.js');
18
+ var CellValue = require('../../../../ListView/components/TableCells/CellValue.js');
19
+ var componentCopy = require('./utils/componentCopy.js');
20
+
21
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
+
23
+ function _interopNamespace(e) {
24
+ if (e && e.__esModule) return e;
25
+ var n = Object.create(null);
26
+ if (e) {
27
+ Object.keys(e).forEach(function (k) {
28
+ if (k !== 'default') {
29
+ var d = Object.getOwnPropertyDescriptor(e, k);
30
+ Object.defineProperty(n, k, d.get ? d : {
31
+ enumerable: true,
32
+ get: function () { return e[k]; }
33
+ });
34
+ }
35
+ });
36
+ }
37
+ n.default = e;
38
+ return Object.freeze(n);
39
+ }
40
+
41
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
42
+ var toPath__default = /*#__PURE__*/_interopDefault(toPath);
43
+
44
+ const ENTRIES_PAGE_SIZE = '10';
45
+ const getDocumentTitle = (document1, mainField, fallback, untitled)=>{
46
+ if (mainField !== 'id') {
47
+ const value = document1?.[mainField];
48
+ if (value !== undefined && value !== null && String(value).trim().length > 0) {
49
+ return String(value);
50
+ }
51
+ }
52
+ return fallback || untitled;
53
+ };
54
+ /**
55
+ * The line of text shown under the title of an entry in the list.
56
+ */ const getEntryMeta = ({ count, fieldLabel, firstInstanceLabel, formatMessage, isCurrent, isFormModified, selection })=>{
57
+ if (isCurrent && isFormModified) {
58
+ return formatMessage({
59
+ id: translations.getTranslation('components.ComponentCopyModal.entries.unsaved'),
60
+ defaultMessage: 'Save your changes to copy from this entry'
61
+ });
62
+ }
63
+ if (count === undefined) {
64
+ return undefined;
65
+ }
66
+ if (count === 0) {
67
+ return isCurrent && selection === 'single' ? formatMessage({
68
+ id: translations.getTranslation('components.ComponentCopyModal.entries.noneOther'),
69
+ defaultMessage: 'No other {label} in this entry'
70
+ }, {
71
+ label: fieldLabel
72
+ }) : formatMessage({
73
+ id: translations.getTranslation('components.ComponentCopyModal.entries.none'),
74
+ defaultMessage: 'No {label} yet'
75
+ }, {
76
+ label: fieldLabel
77
+ });
78
+ }
79
+ if (isCurrent) {
80
+ return formatMessage({
81
+ id: translations.getTranslation('components.ComponentCopyModal.entries.count.saved'),
82
+ defaultMessage: '{count, plural, one {# item} other {# items}} · last saved version'
83
+ }, {
84
+ count
85
+ });
86
+ }
87
+ if (selection === 'single' && count === 1 && firstInstanceLabel) {
88
+ return firstInstanceLabel;
89
+ }
90
+ return formatMessage({
91
+ id: translations.getTranslation('components.ComponentCopyModal.entries.count'),
92
+ defaultMessage: '{count, plural, one {# item} other {# items}}'
93
+ }, {
94
+ count
95
+ });
96
+ };
97
+ /**
98
+ * The modal is mounted while it's open and unmounted when it's closed.
99
+ */ const ComponentCopyModal = ({ componentUid, fieldLabel, maxSelection = Infinity, mode, onClose, onInsert, replacesContent = false, selection, sourceFieldName })=>{
100
+ const { formatMessage } = reactIntl.useIntl();
101
+ const { toggleNotification } = strapiAdmin.useNotification();
102
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(translations.getTranslation);
103
+ const { currentDocument, currentDocumentMeta } = useDocumentContext.useDocumentContext('ComponentCopyModal');
104
+ const { edit: { components: componentLayouts, settings } } = useDocumentLayout.useDocumentLayout(currentDocumentMeta.model);
105
+ const targetValues = strapiAdmin.useForm('ComponentCopyModal', (state)=>state.values);
106
+ const isFormModified = strapiAdmin.useForm('ComponentCopyModal', (state)=>state.modified);
107
+ const [search, setSearch] = React__namespace.useState('');
108
+ const debouncedSearch = useDebounce.useDebounce(search, 300);
109
+ const searchRef = React__namespace.useRef(null);
110
+ const [selectedEntry, setSelectedEntry] = React__namespace.useState(null);
111
+ const [sourceDocument, setSourceDocument] = React__namespace.useState(null);
112
+ const [selectedPaths, setSelectedPaths] = React__namespace.useState([]);
113
+ const [isCopying, setIsCopying] = React__namespace.useState(false);
114
+ // Read while rendering: once mounted, the modal holds the focus
115
+ const [returnFocusTarget] = React__namespace.useState(getReturnFocusTarget);
116
+ /**
117
+ * Loading an entry and copying components are asynchronous, the modal can be closed in the
118
+ * meantime and the user can pick another entry while one loads.
119
+ */ const isMountedRef = React__namespace.useRef(true);
120
+ const selectedDocumentIdRef = React__namespace.useRef(null);
121
+ React__namespace.useEffect(()=>{
122
+ isMountedRef.current = true;
123
+ return ()=>{
124
+ isMountedRef.current = false;
125
+ };
126
+ }, []);
127
+ const [getDocuments, { data: documentsData, error: documentsError, isFetching: isFetchingDocuments, isUninitialized: isDocumentsQueryUninitialized }] = documents.useLazyGetAllDocumentsQuery();
128
+ const [getDocument, { isFetching: isFetchingDocument }] = documents.useLazyGetDocumentQuery();
129
+ const [getAllRelations] = relations.useLazyGetAllRelationsQuery();
130
+ const { components } = currentDocument;
131
+ const componentDisplayName = components[componentUid]?.info?.displayName ?? componentUid;
132
+ const currentDocumentId = currentDocumentMeta.documentId;
133
+ /**
134
+ * The last saved version of the entry being edited: what the API holds for it.
135
+ */ const savedDocument = currentDocument.document;
136
+ /**
137
+ * The list of entries only loads the first level of components: for a field nested in another
138
+ * component, how many components an entry holds is only known once the entry is loaded.
139
+ */ const isNestedField = toPath__default.default(sourceFieldName).length > 1;
140
+ React__namespace.useEffect(()=>{
141
+ if (currentDocumentMeta.collectionType !== collections.COLLECTION_TYPES) {
142
+ return;
143
+ }
144
+ void getDocuments({
145
+ model: currentDocumentMeta.model,
146
+ params: {
147
+ ...currentDocumentMeta.params,
148
+ _q: debouncedSearch || undefined,
149
+ page: '1',
150
+ pageSize: ENTRIES_PAGE_SIZE,
151
+ sort: 'updatedAt:DESC'
152
+ }
153
+ });
154
+ }, [
155
+ currentDocumentMeta.collectionType,
156
+ currentDocumentMeta.model,
157
+ currentDocumentMeta.params,
158
+ debouncedSearch,
159
+ getDocuments
160
+ ]);
161
+ React__namespace.useEffect(()=>{
162
+ if (documentsError) {
163
+ toggleNotification({
164
+ type: 'danger',
165
+ message: formatAPIError(documentsError)
166
+ });
167
+ }
168
+ }, [
169
+ documentsError,
170
+ formatAPIError,
171
+ toggleNotification
172
+ ]);
173
+ const findInstances = React__namespace.useCallback((document1)=>componentCopy.getComponentInstances({
174
+ componentUid,
175
+ components,
176
+ getMainField: (uid)=>componentLayouts[uid]?.settings?.mainField,
177
+ mode,
178
+ schema: currentDocument.schema,
179
+ sourceDocument: document1,
180
+ sourceFieldName,
181
+ targetValues
182
+ }), [
183
+ componentLayouts,
184
+ componentUid,
185
+ components,
186
+ currentDocument.schema,
187
+ mode,
188
+ sourceFieldName,
189
+ targetValues
190
+ ]);
191
+ /**
192
+ * In a field holding one component, the saved version of that very component is not something
193
+ * to copy: the field already holds it.
194
+ */ const filterOwnInstance = React__namespace.useCallback((instances)=>selection === 'single' ? instances.filter((instance)=>instance.sourcePath !== sourceFieldName) : instances, [
195
+ selection,
196
+ sourceFieldName
197
+ ]);
198
+ const currentEntryInstances = React__namespace.useMemo(()=>savedDocument ? filterOwnInstance(findInstances(savedDocument)) : [], [
199
+ filterOwnInstance,
200
+ findInstances,
201
+ savedDocument
202
+ ]);
203
+ const untitled = formatMessage({
204
+ id: 'content-manager.containers.untitled',
205
+ defaultMessage: 'Untitled'
206
+ });
207
+ const getTitle = (document1)=>getDocumentTitle(document1, settings.mainField, document1.documentId, untitled);
208
+ const currentEntryTitle = savedDocument ? getTitle(savedDocument) : '';
209
+ const normalizedSearch = debouncedSearch.trim().toLowerCase();
210
+ // A locale that isn't saved yet has a document id but nothing stored (no `id`)
211
+ const showCurrentEntry = !!savedDocument?.id && !!currentDocumentId && (!normalizedSearch || currentEntryTitle.toLowerCase().includes(normalizedSearch));
212
+ const currentEntryRow = showCurrentEntry ? {
213
+ documentId: currentDocumentId,
214
+ title: currentEntryTitle,
215
+ isCurrent: true,
216
+ disabled: isFormModified || currentEntryInstances.length === 0,
217
+ meta: getEntryMeta({
218
+ count: currentEntryInstances.length,
219
+ fieldLabel,
220
+ formatMessage,
221
+ isCurrent: true,
222
+ isFormModified,
223
+ selection
224
+ })
225
+ } : null;
226
+ const entryRows = [
227
+ ...currentEntryRow ? [
228
+ currentEntryRow
229
+ ] : [],
230
+ ...(documentsData?.results ?? []).filter((document1)=>document1.documentId !== currentDocumentId).map((document1)=>{
231
+ const instances = isNestedField ? undefined : findInstances(document1);
232
+ return {
233
+ documentId: document1.documentId,
234
+ title: getTitle(document1),
235
+ isCurrent: false,
236
+ disabled: instances?.length === 0,
237
+ meta: getEntryMeta({
238
+ count: instances?.length,
239
+ fieldLabel,
240
+ firstInstanceLabel: instances?.[0]?.label,
241
+ formatMessage,
242
+ isCurrent: false,
243
+ isFormModified,
244
+ selection
245
+ })
246
+ };
247
+ })
248
+ ];
249
+ /**
250
+ * Loads the entry components are copied from.
251
+ */ const handleSelectEntry = async (row)=>{
252
+ // Picking the entry that's already selected keeps the components ticked in it.
253
+ if (selectedDocumentIdRef.current === row.documentId) {
254
+ return;
255
+ }
256
+ selectedDocumentIdRef.current = row.documentId;
257
+ setSelectedEntry({
258
+ documentId: row.documentId,
259
+ title: row.title,
260
+ isCurrent: row.isCurrent
261
+ });
262
+ setSelectedPaths([]);
263
+ if (row.isCurrent) {
264
+ setSourceDocument(savedDocument ?? null);
265
+ return;
266
+ }
267
+ setSourceDocument(null);
268
+ const result = await getDocument({
269
+ collectionType: currentDocumentMeta.collectionType,
270
+ documentId: row.documentId,
271
+ model: currentDocumentMeta.model,
272
+ params: currentDocumentMeta.params
273
+ });
274
+ // Another entry was picked in the meantime, or the modal was closed.
275
+ if (!isMountedRef.current || selectedDocumentIdRef.current !== row.documentId) {
276
+ return;
277
+ }
278
+ if (result.error) {
279
+ toggleNotification({
280
+ type: 'danger',
281
+ message: formatAPIError(result.error)
282
+ });
283
+ selectedDocumentIdRef.current = null;
284
+ setSelectedEntry(null);
285
+ return;
286
+ }
287
+ // Without its data the entry can't be previewed: unselect it, so it can be picked again.
288
+ if (!result.data?.data) {
289
+ selectedDocumentIdRef.current = null;
290
+ setSelectedEntry(null);
291
+ return;
292
+ }
293
+ setSourceDocument(result.data.data);
294
+ };
295
+ const isCurrentEntrySelected = selectedEntry?.isCurrent ?? false;
296
+ const instances = React__namespace.useMemo(()=>{
297
+ if (isCurrentEntrySelected) {
298
+ return currentEntryInstances;
299
+ }
300
+ return sourceDocument ? findInstances(sourceDocument) : [];
301
+ }, [
302
+ currentEntryInstances,
303
+ findInstances,
304
+ isCurrentEntrySelected,
305
+ sourceDocument
306
+ ]);
307
+ // An entry holding a single component is selected right away: there's nothing else to pick.
308
+ React__namespace.useEffect(()=>{
309
+ if (instances.length === 1) {
310
+ const [{ sourcePath }] = instances;
311
+ setSelectedPaths((paths)=>paths.length === 1 && paths[0] === sourcePath ? paths : [
312
+ sourcePath
313
+ ]);
314
+ }
315
+ }, [
316
+ instances
317
+ ]);
318
+ const selectedInstances = instances.filter((instance)=>selectedPaths.includes(instance.sourcePath));
319
+ const areAllSelected = instances.length > 0 && selectedInstances.length === instances.length;
320
+ const isSelectionTooLarge = selectedInstances.length > maxSelection;
321
+ const mainField = componentLayouts[componentUid]?.settings?.mainField;
322
+ const previewFields = React__namespace.useMemo(()=>componentLayouts[componentUid]?.layout.flat().filter((field)=>field.name !== mainField).map((field)=>({
323
+ name: field.name,
324
+ label: field.label
325
+ })), [
326
+ componentLayouts,
327
+ componentUid,
328
+ mainField
329
+ ]);
330
+ /**
331
+ * The copy is a snapshot of the component, its relations are connected to the same entries.
332
+ * A relation that can't be loaded is left empty rather than failing the whole copy.
333
+ */ const copyInstance = async (instance)=>{
334
+ const relations = componentCopy.getRelationsToCopy(instance.source, componentUid, components);
335
+ const loadedRelations = await Promise.allSettled(relations.map(({ model, id, targetField })=>getAllRelations({
336
+ model,
337
+ id,
338
+ targetField,
339
+ params: currentDocumentMeta.params
340
+ }).unwrap()));
341
+ const data = loadedRelations.reduce((acc, result, index)=>result.status === 'fulfilled' ? componentCopy.connectRelations(acc, relations[index].path, result.value) : acc, componentCopy.cloneComponentData(instance.source, componentUid, components));
342
+ return {
343
+ data: mode === 'dynamiczone' ? {
344
+ ...data,
345
+ __component: componentUid
346
+ } : data,
347
+ hasMissingRelations: loadedRelations.some((result)=>result.status === 'rejected')
348
+ };
349
+ };
350
+ /**
351
+ * The current entry gets its own messages: "this entry" can't be injected in place of an
352
+ * entry title without breaking the sentence in other languages.
353
+ */ const getSuccessMessage = (count)=>{
354
+ const entry = selectedEntry?.title ?? '';
355
+ if (selection === 'single' && replacesContent) {
356
+ return isCurrentEntrySelected ? formatMessage({
357
+ id: translations.getTranslation('components.ComponentCopyModal.success.replaced.currentEntry'),
358
+ defaultMessage: '{label} replaced with content from this entry'
359
+ }, {
360
+ label: fieldLabel
361
+ }) : formatMessage({
362
+ id: translations.getTranslation('components.ComponentCopyModal.success.replaced'),
363
+ defaultMessage: '{label} replaced with content from {entry}'
364
+ }, {
365
+ label: fieldLabel,
366
+ entry
367
+ });
368
+ }
369
+ if (selection === 'single') {
370
+ return isCurrentEntrySelected ? formatMessage({
371
+ id: translations.getTranslation('components.ComponentCopyModal.success.single.currentEntry'),
372
+ defaultMessage: '{label} copied from this entry'
373
+ }, {
374
+ label: fieldLabel
375
+ }) : formatMessage({
376
+ id: translations.getTranslation('components.ComponentCopyModal.success.single'),
377
+ defaultMessage: '{label} copied from {entry}'
378
+ }, {
379
+ label: fieldLabel,
380
+ entry
381
+ });
382
+ }
383
+ if (mode === 'dynamiczone') {
384
+ return isCurrentEntrySelected ? formatMessage({
385
+ id: translations.getTranslation('components.ComponentCopyModal.success.components.currentEntry'),
386
+ defaultMessage: '{count, plural, one {# {component} component} other {# {component} components}} copied from this entry'
387
+ }, {
388
+ count,
389
+ component: componentDisplayName
390
+ }) : formatMessage({
391
+ id: translations.getTranslation('components.ComponentCopyModal.success.components'),
392
+ defaultMessage: '{count, plural, one {# {component} component} other {# {component} components}} copied from {entry}'
393
+ }, {
394
+ count,
395
+ component: componentDisplayName,
396
+ entry
397
+ });
398
+ }
399
+ return isCurrentEntrySelected ? formatMessage({
400
+ id: translations.getTranslation('components.ComponentCopyModal.success.entries.currentEntry'),
401
+ defaultMessage: '{count, plural, one {# entry} other {# entries}} copied from this entry'
402
+ }, {
403
+ count
404
+ }) : formatMessage({
405
+ id: translations.getTranslation('components.ComponentCopyModal.success.entries'),
406
+ defaultMessage: '{count, plural, one {# entry} other {# entries}} copied from {entry}'
407
+ }, {
408
+ count,
409
+ entry
410
+ });
411
+ };
412
+ const handleConfirm = async ()=>{
413
+ if (selectedInstances.length === 0 || isSelectionTooLarge) {
414
+ return;
415
+ }
416
+ setIsCopying(true);
417
+ const copies = await Promise.all(selectedInstances.map(copyInstance));
418
+ if (!isMountedRef.current) {
419
+ return;
420
+ }
421
+ // The field tells the user why it turns the copies down, e.g. it's full.
422
+ if (!onInsert(copies.map(({ data })=>data))) {
423
+ onClose();
424
+ return;
425
+ }
426
+ onClose();
427
+ toggleNotification(copies.some(({ hasMissingRelations })=>hasMissingRelations) ? {
428
+ type: 'warning',
429
+ message: formatMessage({
430
+ id: translations.getTranslation('components.ComponentCopyModal.relations-warning'),
431
+ defaultMessage: 'Content copied, but some of its relations could not be loaded.'
432
+ })
433
+ } : {
434
+ type: 'success',
435
+ message: getSuccessMessage(copies.length)
436
+ });
437
+ };
438
+ const title = selection === 'single' ? formatMessage({
439
+ id: translations.getTranslation('components.ComponentCopyModal.title.single'),
440
+ defaultMessage: 'Copy an existing {label}'
441
+ }, {
442
+ label: fieldLabel
443
+ }) : formatMessage({
444
+ id: translations.getTranslation('components.ComponentCopyModal.title.multiple'),
445
+ defaultMessage: 'Copy existing {label}'
446
+ }, {
447
+ label: fieldLabel
448
+ });
449
+ const getConfirmLabel = ()=>{
450
+ if (selection === 'single' && replacesContent) {
451
+ return formatMessage({
452
+ id: translations.getTranslation('components.ComponentCopyModal.confirm.replace'),
453
+ defaultMessage: 'Replace content'
454
+ });
455
+ }
456
+ if (selectedInstances.length > 1) {
457
+ return formatMessage({
458
+ id: translations.getTranslation('components.ComponentCopyModal.confirm.multiple'),
459
+ defaultMessage: 'Use content from {count} items'
460
+ }, {
461
+ count: selectedInstances.length
462
+ });
463
+ }
464
+ return formatMessage({
465
+ id: translations.getTranslation('components.ComponentCopyModal.confirm'),
466
+ defaultMessage: 'Use content'
467
+ });
468
+ };
469
+ const renderFooterNote = ()=>{
470
+ if (isSelectionTooLarge) {
471
+ return /*#__PURE__*/ jsxRuntime.jsx(FooterWarning, {
472
+ children: formatMessage({
473
+ id: translations.getTranslation('components.ComponentCopyModal.footer.tooMany'),
474
+ defaultMessage: 'You can add {remaining, plural, one {# more item} other {# more items}} to {label}. Unselect {excess, plural, one {# item} other {# items}}.'
475
+ }, {
476
+ label: fieldLabel,
477
+ remaining: maxSelection,
478
+ excess: selectedInstances.length - maxSelection
479
+ })
480
+ });
481
+ }
482
+ if (selection === 'single' && replacesContent) {
483
+ return /*#__PURE__*/ jsxRuntime.jsx(FooterWarning, {
484
+ children: formatMessage({
485
+ id: translations.getTranslation('components.ComponentCopyModal.footer.replace'),
486
+ defaultMessage: 'This will replace the current content of {label}.'
487
+ }, {
488
+ label: fieldLabel
489
+ })
490
+ });
491
+ }
492
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
493
+ variant: "pi",
494
+ textColor: "neutral600",
495
+ children: formatMessage({
496
+ id: translations.getTranslation('components.ComponentCopyModal.footer.note'),
497
+ defaultMessage: 'The content is copied: editing it here won’t change the original entry.'
498
+ })
499
+ });
500
+ };
501
+ const isLoadingEntries = isFetchingDocuments || isDocumentsQueryUninitialized;
502
+ const isLoadingSource = selectedEntry !== null && !selectedEntry.isCurrent && !sourceDocument;
503
+ const loadingLabel = formatMessage({
504
+ id: translations.getTranslation('components.ComponentCopyModal.loading'),
505
+ defaultMessage: 'Loading'
506
+ });
507
+ const renderInstanceSummary = (instance, titleId)=>/*#__PURE__*/ jsxRuntime.jsx(InstanceSummary, {
508
+ instance: instance,
509
+ preview: componentCopy.getComponentPreview(instance.source, componentUid, components, previewFields),
510
+ titleId: titleId
511
+ });
512
+ const renderPreview = ()=>{
513
+ if (selectedEntry === null) {
514
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
515
+ direction: "column",
516
+ alignItems: "center",
517
+ justifyContent: "center",
518
+ gap: 2,
519
+ padding: 6,
520
+ grow: 1,
521
+ children: [
522
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
523
+ color: "neutral400",
524
+ children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Duplicate, {
525
+ width: "2.4rem",
526
+ height: "2.4rem"
527
+ })
528
+ }),
529
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
530
+ variant: "omega",
531
+ textColor: "neutral600",
532
+ textAlign: "center",
533
+ children: selection === 'single' ? formatMessage({
534
+ id: translations.getTranslation('components.ComponentCopyModal.preview.empty.single'),
535
+ defaultMessage: 'Pick an entry to preview its {label}.'
536
+ }, {
537
+ label: fieldLabel
538
+ }) : formatMessage({
539
+ id: translations.getTranslation('components.ComponentCopyModal.preview.empty'),
540
+ defaultMessage: 'Pick an entry to see its {label}.'
541
+ }, {
542
+ label: fieldLabel
543
+ })
544
+ })
545
+ ]
546
+ });
547
+ }
548
+ if (isLoadingSource) {
549
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
550
+ justifyContent: "center",
551
+ alignItems: "center",
552
+ grow: 1,
553
+ padding: 6,
554
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Loader, {
555
+ small: true,
556
+ children: loadingLabel
557
+ })
558
+ });
559
+ }
560
+ return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
561
+ children: [
562
+ /*#__PURE__*/ jsxRuntime.jsxs(PreviewHeader, {
563
+ justifyContent: "space-between",
564
+ alignItems: "center",
565
+ gap: 4,
566
+ paddingTop: 4,
567
+ paddingBottom: 4,
568
+ paddingLeft: 5,
569
+ paddingRight: 5,
570
+ children: [
571
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
572
+ fontWeight: "bold",
573
+ textColor: "neutral800",
574
+ ellipsis: true,
575
+ children: formatMessage({
576
+ id: translations.getTranslation('components.ComponentCopyModal.preview.title'),
577
+ defaultMessage: '{entry} · {label}'
578
+ }, {
579
+ entry: selectedEntry.title,
580
+ label: fieldLabel
581
+ })
582
+ }),
583
+ selection === 'multiple' && instances.length > 1 && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Checkbox, {
584
+ checked: areAllSelected,
585
+ onCheckedChange: (checked)=>setSelectedPaths(checked === true ? instances.map((instance)=>instance.sourcePath) : []),
586
+ children: formatMessage({
587
+ id: translations.getTranslation('components.ComponentCopyModal.selectAll'),
588
+ defaultMessage: 'Select all'
589
+ })
590
+ })
591
+ ]
592
+ }),
593
+ /*#__PURE__*/ jsxRuntime.jsx(Scrollable, {
594
+ direction: "column",
595
+ alignItems: "stretch",
596
+ gap: 3,
597
+ padding: 5,
598
+ children: /*#__PURE__*/ jsxRuntime.jsx(InstancePicker, {
599
+ fieldLabel: fieldLabel,
600
+ instances: instances,
601
+ onSelectedPathsChange: setSelectedPaths,
602
+ renderSummary: renderInstanceSummary,
603
+ selectedPaths: selectedPaths,
604
+ selection: selection
605
+ })
606
+ })
607
+ ]
608
+ });
609
+ };
610
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Root, {
611
+ open: true,
612
+ onOpenChange: (isOpen)=>!isOpen && onClose(),
613
+ children: /*#__PURE__*/ jsxRuntime.jsxs(ModalContent, {
614
+ onCloseAutoFocus: (event)=>{
615
+ // The modal has no trigger of its own to give the focus back to
616
+ if (returnFocusTarget?.isConnected) {
617
+ event.preventDefault();
618
+ returnFocusTarget.focus();
619
+ }
620
+ },
621
+ onEscapeKeyDown: (event)=>{
622
+ // Escape in a filled search only clears it, which the searchbar does itself
623
+ if (search !== '' && event.target === searchRef.current) {
624
+ event.preventDefault();
625
+ }
626
+ },
627
+ children: [
628
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Header, {
629
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Title, {
630
+ children: title
631
+ })
632
+ }),
633
+ /*#__PURE__*/ jsxRuntime.jsxs(Panes, {
634
+ children: [
635
+ /*#__PURE__*/ jsxRuntime.jsx(EntriesPane, {
636
+ appliedSearch: debouncedSearch,
637
+ isLoading: isLoadingEntries,
638
+ // Picking a second entry while one loads would start a race between the two.
639
+ isSelectionLocked: isFetchingDocument || isCopying,
640
+ onSearchChange: setSearch,
641
+ onSelect: handleSelectEntry,
642
+ rows: entryRows,
643
+ search: search,
644
+ searchRef: searchRef,
645
+ selectedDocumentId: selectedEntry?.documentId ?? null
646
+ }),
647
+ /*#__PURE__*/ jsxRuntime.jsx(PreviewPane, {
648
+ direction: "column",
649
+ alignItems: "stretch",
650
+ children: renderPreview()
651
+ })
652
+ ]
653
+ }),
654
+ /*#__PURE__*/ jsxRuntime.jsxs(ModalFooter, {
655
+ gap: 4,
656
+ children: [
657
+ renderFooterNote(),
658
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
659
+ gap: 2,
660
+ shrink: 0,
661
+ children: [
662
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Close, {
663
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
664
+ variant: "tertiary",
665
+ children: formatMessage({
666
+ id: 'app.components.Button.cancel',
667
+ defaultMessage: 'Cancel'
668
+ })
669
+ })
670
+ }),
671
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
672
+ onClick: handleConfirm,
673
+ disabled: selectedInstances.length === 0 || isSelectionTooLarge,
674
+ loading: isCopying,
675
+ children: getConfirmLabel()
676
+ })
677
+ ]
678
+ })
679
+ ]
680
+ })
681
+ ]
682
+ })
683
+ });
684
+ };
685
+ /* -------------------------------------------------------------------------------------------------
686
+ * getReturnFocusTarget
687
+ * -----------------------------------------------------------------------------------------------*/ /**
688
+ * @description The element to focus once the modal is closed: the button that opened it, or when
689
+ * it was opened from a menu item, the button of the menu (the menu is gone by then).
690
+ */ const getReturnFocusTarget = ()=>{
691
+ const active = document.activeElement;
692
+ if (!(active instanceof HTMLElement) || active === document.body) {
693
+ return null;
694
+ }
695
+ const menu = active.closest('[role="menu"]');
696
+ if (!menu) {
697
+ return active;
698
+ }
699
+ return menu.id ? document.querySelector(`[aria-controls="${CSS.escape(menu.id)}"]`) : null;
700
+ };
701
+ /**
702
+ * The left pane of the modal: the search and the list of entries to copy from.
703
+ */ const EntriesPane = ({ appliedSearch, isLoading, isSelectionLocked, onSearchChange, onSelect, rows, search, searchRef, selectedDocumentId })=>{
704
+ const { formatMessage } = reactIntl.useIntl();
705
+ const normalizedSearch = appliedSearch.trim();
706
+ const searchLabel = formatMessage({
707
+ id: translations.getTranslation('components.ComponentCopyModal.search'),
708
+ defaultMessage: 'Search all entries'
709
+ });
710
+ const renderRows = ()=>{
711
+ if (isLoading) {
712
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
713
+ justifyContent: "center",
714
+ padding: 4,
715
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Loader, {
716
+ small: true,
717
+ children: formatMessage({
718
+ id: translations.getTranslation('components.ComponentCopyModal.loading'),
719
+ defaultMessage: 'Loading'
720
+ })
721
+ })
722
+ });
723
+ }
724
+ if (rows.length === 0) {
725
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
726
+ padding: 2,
727
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
728
+ variant: "omega",
729
+ textColor: "neutral600",
730
+ children: normalizedSearch ? formatMessage({
731
+ id: translations.getTranslation('components.ComponentCopyModal.entries.noMatch'),
732
+ defaultMessage: 'No entry matches “{search}”'
733
+ }, {
734
+ search: normalizedSearch
735
+ }) : formatMessage({
736
+ id: translations.getTranslation('components.ComponentCopyModal.noEntries'),
737
+ defaultMessage: 'No entries found'
738
+ })
739
+ })
740
+ });
741
+ }
742
+ // While locked, the entries aren't `disabled`: the picked one keeps the focus while it loads.
743
+ return rows.map((row)=>/*#__PURE__*/ jsxRuntime.jsxs(EntryButton, {
744
+ type: "button",
745
+ "aria-pressed": row.documentId === selectedDocumentId,
746
+ "aria-disabled": isSelectionLocked || undefined,
747
+ disabled: row.disabled,
748
+ onClick: ()=>{
749
+ if (!isSelectionLocked) {
750
+ onSelect(row);
751
+ }
752
+ },
753
+ children: [
754
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
755
+ gap: 2,
756
+ alignItems: "center",
757
+ wrap: "wrap",
758
+ children: [
759
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
760
+ fontWeight: "bold",
761
+ textColor: "inherit",
762
+ children: row.title
763
+ }),
764
+ row.isCurrent && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Badge, {
765
+ size: "S",
766
+ active: true,
767
+ children: formatMessage({
768
+ id: translations.getTranslation('components.ComponentCopyModal.entries.current'),
769
+ defaultMessage: 'This entry'
770
+ })
771
+ })
772
+ ]
773
+ }),
774
+ row.meta && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
775
+ variant: "pi",
776
+ textColor: "neutral600",
777
+ children: row.meta
778
+ })
779
+ ]
780
+ }, row.documentId));
781
+ };
782
+ return /*#__PURE__*/ jsxRuntime.jsxs(EntriesPaneRoot, {
783
+ direction: "column",
784
+ alignItems: "stretch",
785
+ children: [
786
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
787
+ paddingTop: 4,
788
+ paddingBottom: 2,
789
+ paddingLeft: 4,
790
+ paddingRight: 4,
791
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Searchbar, {
792
+ ref: searchRef,
793
+ name: "component-copy-search",
794
+ value: search,
795
+ onChange: (event)=>onSearchChange(event.target.value),
796
+ onClear: ()=>onSearchChange(''),
797
+ clearLabel: formatMessage({
798
+ id: 'clearLabel',
799
+ defaultMessage: 'Clear'
800
+ }),
801
+ placeholder: searchLabel,
802
+ size: "S",
803
+ children: searchLabel
804
+ })
805
+ }),
806
+ !normalizedSearch && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
807
+ paddingLeft: 4,
808
+ paddingRight: 4,
809
+ paddingBottom: 1,
810
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
811
+ variant: "sigma",
812
+ textColor: "neutral600",
813
+ children: formatMessage({
814
+ id: translations.getTranslation('components.ComponentCopyModal.entries.title'),
815
+ defaultMessage: 'Recently updated entries'
816
+ })
817
+ })
818
+ }),
819
+ /*#__PURE__*/ jsxRuntime.jsx(Scrollable, {
820
+ direction: "column",
821
+ alignItems: "stretch",
822
+ gap: 1,
823
+ padding: 2,
824
+ children: renderRows()
825
+ })
826
+ ]
827
+ });
828
+ };
829
+ /**
830
+ * The components of the picked entry: a read-only card when there's one to take, radios when one
831
+ * of several is taken, checkboxes when several can be taken.
832
+ */ const InstancePicker = ({ fieldLabel, instances, onSelectedPathsChange, renderSummary, selectedPaths, selection })=>{
833
+ const { formatMessage } = reactIntl.useIntl();
834
+ const radioTitleId = React__namespace.useId();
835
+ if (instances.length === 0) {
836
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
837
+ variant: "omega",
838
+ textColor: "neutral600",
839
+ children: formatMessage({
840
+ id: translations.getTranslation('components.ComponentCopyModal.preview.noComponents'),
841
+ defaultMessage: 'No {label} in this entry'
842
+ }, {
843
+ label: fieldLabel
844
+ })
845
+ });
846
+ }
847
+ if (selection === 'single' && instances.length === 1) {
848
+ return /*#__PURE__*/ jsxRuntime.jsx(InstanceCard, {
849
+ $selected: true,
850
+ children: renderSummary(instances[0], `${radioTitleId}-0`)
851
+ });
852
+ }
853
+ if (selection === 'single') {
854
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Radio.Group, {
855
+ value: selectedPaths[0] ?? '',
856
+ onValueChange: (value)=>onSelectedPathsChange([
857
+ value
858
+ ]),
859
+ children: instances.map((instance, index)=>{
860
+ const titleId = `${radioTitleId}-${index}`;
861
+ return /*#__PURE__*/ jsxRuntime.jsx(InstanceCard, {
862
+ $selected: selectedPaths.includes(instance.sourcePath),
863
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Radio.Item, {
864
+ value: instance.sourcePath,
865
+ "aria-labelledby": titleId,
866
+ children: renderSummary(instance, titleId)
867
+ })
868
+ }, instance.sourcePath);
869
+ })
870
+ });
871
+ }
872
+ const togglePath = (sourcePath, checked)=>{
873
+ onSelectedPathsChange(checked ? [
874
+ ...selectedPaths.filter((path)=>path !== sourcePath),
875
+ sourcePath
876
+ ] : selectedPaths.filter((path)=>path !== sourcePath));
877
+ };
878
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
879
+ children: instances.map((instance)=>/*#__PURE__*/ jsxRuntime.jsx(SelectableInstanceCard, {
880
+ checked: selectedPaths.includes(instance.sourcePath),
881
+ onCheckedChange: (checked)=>togglePath(instance.sourcePath, checked),
882
+ renderSummary: (titleId)=>renderSummary(instance, titleId)
883
+ }, instance.sourcePath))
884
+ });
885
+ };
886
+ const InstanceSummary = ({ instance, preview, titleId })=>{
887
+ const { formatMessage } = reactIntl.useIntl();
888
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
889
+ tag: "span",
890
+ direction: "column",
891
+ alignItems: "stretch",
892
+ gap: 2,
893
+ grow: 1,
894
+ minWidth: 0,
895
+ children: [
896
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
897
+ tag: "span",
898
+ gap: 2,
899
+ alignItems: "baseline",
900
+ wrap: "wrap",
901
+ children: [
902
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
903
+ tag: "span",
904
+ id: titleId,
905
+ fontWeight: "bold",
906
+ textColor: "neutral800",
907
+ children: instance.label
908
+ }),
909
+ instance.parentLabel && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
910
+ tag: "span",
911
+ variant: "pi",
912
+ textColor: "neutral600",
913
+ children: formatMessage({
914
+ id: translations.getTranslation('components.ComponentCopyModal.instance.nested'),
915
+ defaultMessage: 'In {parent}'
916
+ }, {
917
+ parent: instance.parentLabel
918
+ })
919
+ })
920
+ ]
921
+ }),
922
+ preview.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(PreviewGrid, {
923
+ tag: "span",
924
+ children: preview.map((field)=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
925
+ tag: "span",
926
+ direction: "column",
927
+ alignItems: "stretch",
928
+ minWidth: 0,
929
+ children: [
930
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
931
+ tag: "span",
932
+ variant: "sigma",
933
+ textColor: "neutral600",
934
+ children: field.label
935
+ }),
936
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
937
+ tag: "span",
938
+ variant: "omega",
939
+ textColor: "neutral800",
940
+ ellipsis: true,
941
+ children: /*#__PURE__*/ jsxRuntime.jsx(CellValue.CellValue, {
942
+ type: field.type,
943
+ value: field.value
944
+ })
945
+ })
946
+ ]
947
+ }, field.name))
948
+ })
949
+ ]
950
+ });
951
+ };
952
+ /**
953
+ * The whole card toggles its checkbox. The checkbox is named after the title of the component
954
+ * only, not after the whole preview.
955
+ */ const SelectableInstanceCard = ({ checked, onCheckedChange, renderSummary })=>{
956
+ const id = React__namespace.useId();
957
+ const titleId = `${id}-title`;
958
+ return /*#__PURE__*/ jsxRuntime.jsx(InstanceCard, {
959
+ tag: "label",
960
+ htmlFor: id,
961
+ $selected: checked,
962
+ $interactive: true,
963
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
964
+ tag: "span",
965
+ gap: 3,
966
+ alignItems: "flex-start",
967
+ children: [
968
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Checkbox, {
969
+ id: id,
970
+ "aria-labelledby": titleId,
971
+ checked: checked,
972
+ onCheckedChange: (value)=>onCheckedChange(value === true)
973
+ }),
974
+ renderSummary(titleId)
975
+ ]
976
+ })
977
+ });
978
+ };
979
+ /* -------------------------------------------------------------------------------------------------
980
+ * Styles
981
+ * -----------------------------------------------------------------------------------------------*/ const FooterWarning = ({ children })=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
982
+ gap: 2,
983
+ alignItems: "center",
984
+ color: "warning600",
985
+ children: [
986
+ /*#__PURE__*/ jsxRuntime.jsx(Icons.WarningCircle, {
987
+ width: "1.6rem",
988
+ height: "1.6rem",
989
+ "aria-hidden": true
990
+ }),
991
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
992
+ variant: "pi",
993
+ textColor: "warning600",
994
+ children: children
995
+ })
996
+ ]
997
+ });
998
+ const ModalContent = styledComponents.styled(designSystem.Modal.Content)`
999
+ width: 92rem;
1000
+ max-width: calc(100% - ${({ theme })=>theme.spaces[8]});
1001
+ height: 60rem;
1002
+ `;
1003
+ const ModalFooter = styledComponents.styled(designSystem.Modal.Footer)`
1004
+ flex: 0 0 auto;
1005
+ `;
1006
+ const Panes = styledComponents.styled.div`
1007
+ display: flex;
1008
+ flex-direction: column;
1009
+ flex: 1;
1010
+ min-height: 0;
1011
+
1012
+ ${({ theme })=>theme.breakpoints.medium} {
1013
+ flex-direction: row;
1014
+ }
1015
+ `;
1016
+ const EntriesPaneRoot = styledComponents.styled(designSystem.Flex)`
1017
+ flex: 0 0 auto;
1018
+ max-height: 40%;
1019
+ min-height: 0;
1020
+ border-bottom: 1px solid ${({ theme })=>theme.colors.neutral150};
1021
+
1022
+ ${({ theme })=>theme.breakpoints.medium} {
1023
+ width: 30rem;
1024
+ max-height: none;
1025
+ border-bottom: 0;
1026
+ border-right: 1px solid ${({ theme })=>theme.colors.neutral150};
1027
+ }
1028
+ `;
1029
+ const PreviewPane = styledComponents.styled(designSystem.Flex)`
1030
+ flex: 1;
1031
+ min-width: 0;
1032
+ min-height: 0;
1033
+ `;
1034
+ const Scrollable = styledComponents.styled(designSystem.Flex)`
1035
+ flex: 1;
1036
+ min-height: 0;
1037
+ overflow-y: auto;
1038
+ `;
1039
+ const PreviewHeader = styledComponents.styled(designSystem.Flex)`
1040
+ border-bottom: 1px solid ${({ theme })=>theme.colors.neutral150};
1041
+ `;
1042
+ const EntryButton = styledComponents.styled.button`
1043
+ display: flex;
1044
+ flex-direction: column;
1045
+ align-items: flex-start;
1046
+ gap: ${({ theme })=>theme.spaces[1]};
1047
+ width: 100%;
1048
+ border: 1px solid transparent;
1049
+ border-radius: ${({ theme })=>theme.borderRadius};
1050
+ background: transparent;
1051
+ color: ${({ theme })=>theme.colors.neutral800};
1052
+ padding: ${({ theme })=>theme.spaces[2]} ${({ theme })=>theme.spaces[3]};
1053
+ text-align: left;
1054
+ cursor: pointer;
1055
+
1056
+ &:not(:disabled):not([aria-disabled='true']):hover {
1057
+ background: ${({ theme })=>theme.colors.neutral100};
1058
+ }
1059
+
1060
+ &:not(:disabled):focus-visible {
1061
+ outline: 2px solid ${({ theme })=>theme.colors.primary600};
1062
+ outline-offset: -2px;
1063
+ }
1064
+
1065
+ &[aria-pressed='true'] {
1066
+ border-color: ${({ theme })=>theme.colors.primary200};
1067
+ background: ${({ theme })=>theme.colors.primary100};
1068
+ }
1069
+
1070
+ &:disabled,
1071
+ &[aria-disabled='true'] {
1072
+ cursor: not-allowed;
1073
+ color: ${({ theme })=>theme.colors.neutral500};
1074
+ }
1075
+ `;
1076
+ const InstanceCard = styledComponents.styled(designSystem.Box)`
1077
+ display: block;
1078
+ border: 1px solid
1079
+ ${({ theme, $selected })=>$selected ? theme.colors.primary600 : theme.colors.neutral200};
1080
+ border-radius: ${({ theme })=>theme.borderRadius};
1081
+ background: ${({ theme, $selected })=>$selected ? theme.colors.primary100 : theme.colors.neutral100};
1082
+ padding: ${({ theme })=>theme.spaces[4]};
1083
+ cursor: ${({ $interactive })=>$interactive ? 'pointer' : 'default'};
1084
+
1085
+ @media (prefers-reduced-motion: no-preference) {
1086
+ transition:
1087
+ border-color 120ms ${({ theme })=>theme.motion.easings.easeOutQuad},
1088
+ background-color 120ms ${({ theme })=>theme.motion.easings.easeOutQuad};
1089
+ }
1090
+
1091
+ &:hover {
1092
+ border-color: ${({ theme, $interactive, $selected })=>$interactive && !$selected ? theme.colors.primary200 : undefined};
1093
+ }
1094
+ `;
1095
+ const PreviewGrid = styledComponents.styled(designSystem.Box)`
1096
+ display: grid;
1097
+ grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
1098
+ gap: ${({ theme })=>theme.spaces[2]} ${({ theme })=>theme.spaces[4]};
1099
+ `;
1100
+
1101
+ exports.ComponentCopyModal = ComponentCopyModal;
1102
+ exports.getEntryMeta = getEntryMeta;
1103
+ //# sourceMappingURL=ComponentCopyModal.js.map