@strapi/content-manager 5.12.3 → 5.12.5

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 (100) hide show
  1. package/dist/admin/components/Widgets.js +161 -0
  2. package/dist/admin/components/Widgets.js.map +1 -0
  3. package/dist/admin/components/Widgets.mjs +158 -0
  4. package/dist/admin/components/Widgets.mjs.map +1 -0
  5. package/dist/admin/content-manager.js.map +1 -1
  6. package/dist/admin/content-manager.mjs.map +1 -1
  7. package/dist/admin/history/pages/History.js +1 -9
  8. package/dist/admin/history/pages/History.js.map +1 -1
  9. package/dist/admin/history/pages/History.mjs +1 -9
  10. package/dist/admin/history/pages/History.mjs.map +1 -1
  11. package/dist/admin/hooks/useDocumentActions.js +8 -1
  12. package/dist/admin/hooks/useDocumentActions.js.map +1 -1
  13. package/dist/admin/hooks/useDocumentActions.mjs +8 -1
  14. package/dist/admin/hooks/useDocumentActions.mjs.map +1 -1
  15. package/dist/admin/hooks/useDocumentContext.js +57 -0
  16. package/dist/admin/hooks/useDocumentContext.js.map +1 -0
  17. package/dist/admin/hooks/useDocumentContext.mjs +36 -0
  18. package/dist/admin/hooks/useDocumentContext.mjs.map +1 -0
  19. package/dist/admin/index.js +41 -2
  20. package/dist/admin/index.js.map +1 -1
  21. package/dist/admin/index.mjs +42 -3
  22. package/dist/admin/index.mjs.map +1 -1
  23. package/dist/admin/pages/EditView/EditViewPage.js +85 -93
  24. package/dist/admin/pages/EditView/EditViewPage.js.map +1 -1
  25. package/dist/admin/pages/EditView/EditViewPage.mjs +86 -94
  26. package/dist/admin/pages/EditView/EditViewPage.mjs.map +1 -1
  27. package/dist/admin/pages/EditView/components/DocumentActions.js +28 -32
  28. package/dist/admin/pages/EditView/components/DocumentActions.js.map +1 -1
  29. package/dist/admin/pages/EditView/components/DocumentActions.mjs +32 -36
  30. package/dist/admin/pages/EditView/components/DocumentActions.mjs.map +1 -1
  31. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.js +2 -2
  32. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.js.map +1 -1
  33. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.mjs +2 -2
  34. package/dist/admin/pages/EditView/components/FormInputs/Component/Input.mjs.map +1 -1
  35. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.js +2 -2
  36. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.js.map +1 -1
  37. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.mjs +2 -2
  38. package/dist/admin/pages/EditView/components/FormInputs/Component/NonRepeatable.mjs.map +1 -1
  39. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js +2 -2
  40. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.js.map +1 -1
  41. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs +2 -2
  42. package/dist/admin/pages/EditView/components/FormInputs/Component/Repeatable.mjs.map +1 -1
  43. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js +5 -16
  44. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js.map +1 -1
  45. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs +5 -16
  46. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs.map +1 -1
  47. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js +2 -3
  48. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js.map +1 -1
  49. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs +2 -3
  50. package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs.map +1 -1
  51. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js +432 -344
  52. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js.map +1 -1
  53. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs +432 -346
  54. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs.map +1 -1
  55. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +55 -37
  56. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
  57. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +57 -39
  58. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
  59. package/dist/admin/pages/EditView/components/FormInputs/UID.js +7 -7
  60. package/dist/admin/pages/EditView/components/FormInputs/UID.js.map +1 -1
  61. package/dist/admin/pages/EditView/components/FormInputs/UID.mjs +7 -7
  62. package/dist/admin/pages/EditView/components/FormInputs/UID.mjs.map +1 -1
  63. package/dist/admin/pages/EditView/components/InputRenderer.js +3 -10
  64. package/dist/admin/pages/EditView/components/InputRenderer.js.map +1 -1
  65. package/dist/admin/pages/EditView/components/InputRenderer.mjs +3 -10
  66. package/dist/admin/pages/EditView/components/InputRenderer.mjs.map +1 -1
  67. package/dist/admin/preview/components/PreviewHeader.js +2 -5
  68. package/dist/admin/preview/components/PreviewHeader.js.map +1 -1
  69. package/dist/admin/preview/components/PreviewHeader.mjs +2 -5
  70. package/dist/admin/preview/components/PreviewHeader.mjs.map +1 -1
  71. package/dist/admin/preview/pages/Preview.js +94 -102
  72. package/dist/admin/preview/pages/Preview.js.map +1 -1
  73. package/dist/admin/preview/pages/Preview.mjs +94 -102
  74. package/dist/admin/preview/pages/Preview.mjs.map +1 -1
  75. package/dist/admin/services/homepage.js +26 -0
  76. package/dist/admin/services/homepage.js.map +1 -0
  77. package/dist/admin/services/homepage.mjs +24 -0
  78. package/dist/admin/services/homepage.mjs.map +1 -0
  79. package/dist/admin/src/components/Widgets.d.ts +3 -0
  80. package/dist/admin/src/content-manager.d.ts +0 -3
  81. package/dist/admin/src/features/DocumentRBAC.d.ts +1 -1
  82. package/dist/admin/src/history/pages/History.d.ts +1 -1
  83. package/dist/admin/src/hooks/useDocumentContext.d.ts +30 -0
  84. package/dist/admin/src/pages/EditView/components/FormInputs/ComponentContext.d.ts +1 -1
  85. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  86. package/dist/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.d.ts +50 -5
  87. package/dist/admin/src/pages/EditView/components/FormInputs/Relations/Relations.d.ts +1 -0
  88. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +1 -1
  89. package/dist/admin/src/preview/pages/Preview.d.ts +2 -1
  90. package/dist/admin/src/services/homepage.d.ts +5 -0
  91. package/dist/server/src/services/homepage.d.ts +11 -0
  92. package/dist/server/src/services/homepage.d.ts.map +1 -0
  93. package/dist/shared/contracts/homepage.d.ts +25 -0
  94. package/dist/shared/contracts/homepage.d.ts.map +1 -0
  95. package/package.json +8 -8
  96. package/dist/admin/features/DocumentContext.js +0 -71
  97. package/dist/admin/features/DocumentContext.js.map +0 -1
  98. package/dist/admin/features/DocumentContext.mjs +0 -49
  99. package/dist/admin/features/DocumentContext.mjs.map +0 -1
  100. package/dist/admin/src/features/DocumentContext.d.ts +0 -53
@@ -1,20 +1,22 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import { createContext, Form, ConfirmDialog, useStrapiApp, useRBAC, DescriptionComponentRenderer } from '@strapi/admin/strapi-admin';
4
- import { Modal, TextButton, Tooltip, Flex, IconButton, Typography, Button, Dialog, Loader, EmptyStateLayout, Box } from '@strapi/design-system';
3
+ import { createContext, useQueryParams, Form, useForm, ConfirmDialog, useStrapiApp, useRBAC, DescriptionComponentRenderer } from '@strapi/admin/strapi-admin';
4
+ import { Modal, TextButton, Flex, IconButton, Typography, Dialog, Loader, EmptyStateLayout, Box } from '@strapi/design-system';
5
5
  import { ArrowLeft, ArrowsOut, WarningCircle } from '@strapi/icons';
6
6
  import { useIntl } from 'react-intl';
7
7
  import { useNavigate, useLocation } from 'react-router-dom';
8
8
  import { styled } from 'styled-components';
9
9
  import { COLLECTION_TYPES, SINGLE_TYPES } from '../../../../../constants/collections.mjs';
10
10
  import { PERMISSIONS } from '../../../../../constants/plugin.mjs';
11
- import { useDocumentContext } from '../../../../../features/DocumentContext.mjs';
12
- import { DocumentRBAC } from '../../../../../features/DocumentRBAC.mjs';
13
- import { useDocumentLayout } from '../../../../../hooks/useDocumentLayout.mjs';
11
+ import { buildValidParams } from '../../../../../utils/api.mjs';
12
+ import { DocumentStatus } from '../../DocumentStatus.mjs';
13
+ import { useDoc, useDocument } from '../../../../../hooks/useDocument.mjs';
14
+ import '../../../../../preview/pages/Preview.mjs';
14
15
  import { useLazyGetDocumentQuery } from '../../../../../services/documents.mjs';
16
+ import { useDocumentLayout } from '../../../../../hooks/useDocumentLayout.mjs';
17
+ import { DocumentRBAC } from '../../../../../features/DocumentRBAC.mjs';
15
18
  import { createYupSchema } from '../../../../../utils/validation.mjs';
16
19
  import { DocumentActionButton } from '../../DocumentActions.mjs';
17
- import { DocumentStatus } from '../../DocumentStatus.mjs';
18
20
  import { FormLayout } from '../../FormLayout.mjs';
19
21
 
20
22
  function getCollectionType(url) {
@@ -22,271 +24,361 @@ function getCollectionType(url) {
22
24
  const match = url.match(regex);
23
25
  return match ? match[1] : undefined;
24
26
  }
25
- const CustomModalContent = styled(Modal.Content)`
27
+ const StyledModalContent = styled(Modal.Content)`
26
28
  width: 90%;
27
29
  max-width: 100%;
28
30
  height: 90%;
29
31
  max-height: 100%;
30
32
  `;
31
- const [RelationModalProvider, useRelationModal] = createContext('RelationModal', {
32
- parentModified: false,
33
- depth: 0
34
- });
35
- const RelationModalForm = ({ relation, triggerButtonLabel })=>{
33
+ function reducer(state, action) {
34
+ switch(action.type){
35
+ case 'GO_TO_RELATION':
36
+ if (state.hasUnsavedChanges && !action.payload.shouldBypassConfirmation) {
37
+ return {
38
+ ...state,
39
+ confirmDialogIntent: action.payload.document
40
+ };
41
+ }
42
+ return {
43
+ ...state,
44
+ documentHistory: [
45
+ ...state.documentHistory,
46
+ action.payload.document
47
+ ],
48
+ confirmDialogIntent: null,
49
+ isModalOpen: true
50
+ };
51
+ case 'GO_BACK':
52
+ if (state.hasUnsavedChanges && !action.payload.shouldBypassConfirmation) {
53
+ return {
54
+ ...state,
55
+ confirmDialogIntent: 'back'
56
+ };
57
+ }
58
+ return {
59
+ ...state,
60
+ documentHistory: state.documentHistory.slice(0, -1),
61
+ confirmDialogIntent: null
62
+ };
63
+ case 'GO_FULL_PAGE':
64
+ if (state.hasUnsavedChanges) {
65
+ return {
66
+ ...state,
67
+ confirmDialogIntent: 'navigate'
68
+ };
69
+ }
70
+ return {
71
+ ...state,
72
+ documentHistory: [],
73
+ hasUnsavedChanges: false,
74
+ isModalOpen: false,
75
+ confirmDialogIntent: null
76
+ };
77
+ case 'CANCEL_CONFIRM_DIALOG':
78
+ return {
79
+ ...state,
80
+ confirmDialogIntent: null
81
+ };
82
+ case 'CLOSE_MODAL':
83
+ if (state.hasUnsavedChanges && !action.payload.shouldBypassConfirmation) {
84
+ return {
85
+ ...state,
86
+ confirmDialogIntent: 'close'
87
+ };
88
+ }
89
+ return {
90
+ ...state,
91
+ documentHistory: [],
92
+ confirmDialogIntent: null,
93
+ hasUnsavedChanges: false,
94
+ isModalOpen: false
95
+ };
96
+ case 'SET_HAS_UNSAVED_CHANGES':
97
+ return {
98
+ ...state,
99
+ hasUnsavedChanges: action.payload.hasUnsavedChanges
100
+ };
101
+ default:
102
+ return state;
103
+ }
104
+ }
105
+ const [RelationModalProvider, useRelationModal] = createContext('RelationModal');
106
+ const getFullPageUrl = (currentDocumentMeta)=>{
107
+ const isSingleType = currentDocumentMeta.collectionType === SINGLE_TYPES;
108
+ const queryParams = currentDocumentMeta.params?.locale ? `?plugins[i18n][locale]=${currentDocumentMeta.params.locale}` : '';
109
+ return `/content-manager/${currentDocumentMeta.collectionType}/${currentDocumentMeta.model}${isSingleType ? '' : '/' + currentDocumentMeta.documentId}${queryParams}`;
110
+ };
111
+ /**
112
+ * Component responsible of rendering its children wrapped in a modal, form and context if needed
113
+ */ const RelationModalRenderer = ({ children, trigger, relation })=>{
114
+ const { formatMessage } = useIntl();
115
+ const navigate = useNavigate();
116
+ const [state, dispatch] = React.useReducer(reducer, {
117
+ documentHistory: [],
118
+ confirmDialogIntent: null,
119
+ isModalOpen: false,
120
+ hasUnsavedChanges: false
121
+ });
122
+ const rootDocument = useDoc();
123
+ const [{ query }] = useQueryParams();
124
+ const params = React.useMemo(()=>buildValidParams(query ?? {}), [
125
+ query
126
+ ]);
127
+ const rootDocumentMeta = {
128
+ documentId: rootDocument.document?.documentId || '',
129
+ model: rootDocument.model,
130
+ collectionType: rootDocument.collectionType,
131
+ params
132
+ };
133
+ const currentDocumentMeta = state.documentHistory.at(-1) ?? rootDocumentMeta;
134
+ const currentDocument = useDocument(currentDocumentMeta);
135
+ const parentContextValue = useRelationModal('RelationContextWrapper', (state)=>state, false);
136
+ // A parent relation is already rendering a modal. In this case simply render the trigger
137
+ if (parentContextValue) {
138
+ return trigger;
139
+ }
140
+ /**
141
+ * There is no parent relation, so the relation modal doesn't exist. Create it and set up all the
142
+ * pieces that will be used by potential child relations: the context, header, form, and footer.
143
+ */ return /*#__PURE__*/ jsx(RelationModalProvider, {
144
+ state: state,
145
+ dispatch: dispatch,
146
+ rootDocumentMeta: rootDocumentMeta,
147
+ currentDocumentMeta: currentDocumentMeta,
148
+ currentDocument: currentDocument,
149
+ children: /*#__PURE__*/ jsxs(Modal.Root, {
150
+ open: state.isModalOpen,
151
+ onOpenChange: (open)=>{
152
+ if (open) {
153
+ dispatch({
154
+ type: 'GO_TO_RELATION',
155
+ payload: {
156
+ document: relation,
157
+ shouldBypassConfirmation: false
158
+ }
159
+ });
160
+ } else {
161
+ dispatch({
162
+ type: 'CLOSE_MODAL',
163
+ payload: {
164
+ shouldBypassConfirmation: false
165
+ }
166
+ });
167
+ }
168
+ },
169
+ children: [
170
+ trigger,
171
+ /*#__PURE__*/ jsxs(StyledModalContent, {
172
+ children: [
173
+ /*#__PURE__*/ jsx(Modal.Header, {
174
+ gap: 2,
175
+ children: /*#__PURE__*/ jsxs(Flex, {
176
+ justifyContent: "space-between",
177
+ alignItems: "center",
178
+ width: "100%",
179
+ children: [
180
+ /*#__PURE__*/ jsxs(Flex, {
181
+ gap: 2,
182
+ children: [
183
+ /*#__PURE__*/ jsx(IconButton, {
184
+ withTooltip: false,
185
+ label: formatMessage({
186
+ id: 'global.back',
187
+ defaultMessage: 'Back'
188
+ }),
189
+ variant: "ghost",
190
+ disabled: state.documentHistory.length < 2,
191
+ onClick: ()=>{
192
+ dispatch({
193
+ type: 'GO_BACK',
194
+ payload: {
195
+ shouldBypassConfirmation: false
196
+ }
197
+ });
198
+ },
199
+ marginRight: 1,
200
+ children: /*#__PURE__*/ jsx(ArrowLeft, {})
201
+ }),
202
+ /*#__PURE__*/ jsx(Typography, {
203
+ tag: "span",
204
+ fontWeight: 600,
205
+ children: formatMessage({
206
+ id: 'content-manager.components.RelationInputModal.modal-title',
207
+ defaultMessage: 'Edit a relation'
208
+ })
209
+ })
210
+ ]
211
+ }),
212
+ /*#__PURE__*/ jsx(IconButton, {
213
+ onClick: ()=>{
214
+ dispatch({
215
+ type: 'GO_FULL_PAGE'
216
+ });
217
+ if (!state.hasUnsavedChanges) {
218
+ navigate(getFullPageUrl(currentDocumentMeta));
219
+ }
220
+ },
221
+ variant: "tertiary",
222
+ label: formatMessage({
223
+ id: 'content-manager.components.RelationInputModal.button-fullpage',
224
+ defaultMessage: 'Go to entry'
225
+ }),
226
+ children: /*#__PURE__*/ jsx(ArrowsOut, {})
227
+ })
228
+ ]
229
+ })
230
+ }),
231
+ /*#__PURE__*/ jsx(Modal.Body, {
232
+ children: /*#__PURE__*/ jsx(Form, {
233
+ method: "PUT",
234
+ initialValues: currentDocument.getInitialFormValues(),
235
+ validate: (values, options)=>{
236
+ const yupSchema = createYupSchema(currentDocument.schema?.attributes, currentDocument.components, {
237
+ status: currentDocument.document?.status,
238
+ ...options
239
+ });
240
+ return yupSchema.validate(values, {
241
+ abortEarly: false
242
+ });
243
+ },
244
+ children: children
245
+ })
246
+ })
247
+ ]
248
+ })
249
+ ]
250
+ })
251
+ });
252
+ };
253
+ /**
254
+ * All the main content (not header and footer) of the relation modal, plus the confirmation dialog.
255
+ * Will be wrapped in a Modal.Body by the RelationModalRenderer.
256
+ * Cannot be moved directly inside RelationModal because it needs access to the context via hooks.
257
+ */ const RelationModalBody = ()=>{
36
258
  const navigate = useNavigate();
37
259
  const { pathname, search } = useLocation();
38
260
  const { formatMessage } = useIntl();
39
261
  const [triggerRefetchDocument] = useLazyGetDocumentQuery();
40
- const currentDocument = useDocumentContext('RelationModalForm', (state)=>state.document);
41
- const rootDocumentMeta = useDocumentContext('RelationModalForm', (state)=>state.rootDocumentMeta);
42
- const currentDocumentMeta = useDocumentContext('RelationModalForm', (state)=>state.meta);
43
- const changeDocument = useDocumentContext('RelationModalForm', (state)=>state.changeDocument);
44
- const documentHistory = useDocumentContext('RelationModalForm', (state)=>state.documentHistory);
45
- const setDocumentHistory = useDocumentContext('RelationModalForm', (state)=>state.setDocumentHistory);
46
- const [isConfirmationOpen, setIsConfirmationOpen] = React.useState(false);
47
- const [actionPosition, setActionPosition] = React.useState('cancel');
48
- const [isModalOpen, setIsModalOpen] = React.useState(false);
49
- // NOTE: Not sure about this relation modal context, maybe we should move this to DocumentContext?
50
- // Get parent modal context if it exists
51
- const parentContext = useRelationModal('RelationModalForm', (state)=>state);
52
- // Get depth of nested modals
53
- const depth = parentContext ? parentContext.depth + 1 : 0;
54
- // Check if this is a nested modal
55
- const isNested = depth > 0;
56
- const addDocumentToHistory = (document)=>setDocumentHistory((prev)=>[
57
- ...prev,
58
- document
59
- ]);
60
- const getPreviousDocument = ()=>{
61
- if (documentHistory.length === 0) return undefined;
62
- const lastDocument = documentHistory[documentHistory.length - 1];
63
- return lastDocument;
64
- };
65
- const removeLastDocumentFromHistory = ()=>{
66
- setDocumentHistory((prev)=>[
67
- ...prev
68
- ].slice(0, prev.length - 1));
69
- };
70
- const handleToggleModal = ()=>{
71
- if (isModalOpen) {
72
- setIsModalOpen(false);
73
- const document = {
74
- collectionType: rootDocumentMeta.collectionType,
75
- model: rootDocumentMeta.model,
76
- documentId: rootDocumentMeta.documentId
77
- };
78
- // Change back to the root document
79
- changeDocument(document);
80
- // Reset the document history
81
- setDocumentHistory([]);
82
- // Reset action position
83
- setActionPosition('cancel');
84
- // Read from cache or refetch root document
85
- triggerRefetchDocument(document, // Favor the cache
262
+ const state = useRelationModal('RelationModalForm', (state)=>state.state);
263
+ const dispatch = useRelationModal('RelationModalForm', (state)=>state.dispatch);
264
+ const rootDocumentMeta = useRelationModal('RelationModalForm', (state)=>state.rootDocumentMeta);
265
+ const currentDocumentMeta = useRelationModal('RelationModalForm', (state)=>state.currentDocumentMeta);
266
+ /**
267
+ * One-way sync the modified state from the form to the modal state.
268
+ * It is needed because we need to consume state from the form context in order to lift it up
269
+ * into the modal context. It is not possible otherwise because the modal needs the form state,
270
+ * but it must be a parent of the form.
271
+ */ const modified = useForm('FormWatcher', (state)=>state.modified);
272
+ const isSubmitting = useForm('FormWatcher', (state)=>state.isSubmitting);
273
+ const hasUnsavedChanges = modified && !isSubmitting;
274
+ React.useEffect(()=>{
275
+ dispatch({
276
+ type: 'SET_HAS_UNSAVED_CHANGES',
277
+ payload: {
278
+ hasUnsavedChanges
279
+ }
280
+ });
281
+ }, [
282
+ hasUnsavedChanges,
283
+ dispatch
284
+ ]);
285
+ const handleCloseModal = (shouldBypassConfirmation)=>{
286
+ dispatch({
287
+ type: 'CLOSE_MODAL',
288
+ payload: {
289
+ shouldBypassConfirmation
290
+ }
291
+ });
292
+ {
293
+ // TODO: check if we can avoid this by relying on RTK invalidatesTags.
294
+ // If so we can delete this function and dispatch the events directly
295
+ triggerRefetchDocument(// TODO check if params should be removed (as they were before)
296
+ rootDocumentMeta, // Favor the cache
86
297
  true);
87
- } else {
88
- changeDocument(relation);
89
- setIsModalOpen(true);
90
298
  }
91
299
  };
92
- const getFullPageLink = ()=>{
93
- const isSingleType = currentDocumentMeta.collectionType === SINGLE_TYPES;
94
- const queryParams = currentDocumentMeta.params?.locale ? `?plugins[i18n][locale]=${currentDocumentMeta.params.locale}` : '';
95
- return `/content-manager/${currentDocumentMeta.collectionType}/${currentDocumentMeta.model}${isSingleType ? '' : '/' + currentDocumentMeta.documentId}${queryParams}`;
96
- };
97
300
  const handleRedirection = ()=>{
98
301
  const editViewUrl = `${pathname}${search}`;
99
- const isRootDocumentUrl = editViewUrl.includes(getFullPageLink());
302
+ const fullPageUrl = getFullPageUrl(currentDocumentMeta);
303
+ const isRootDocumentUrl = editViewUrl.includes(fullPageUrl);
100
304
  if (isRootDocumentUrl) {
101
- handleToggleModal();
305
+ handleCloseModal(true);
102
306
  } else {
103
- navigate(getFullPageLink());
307
+ navigate(fullPageUrl);
104
308
  }
105
309
  };
106
310
  const handleConfirm = ()=>{
107
- if (actionPosition === 'navigate') {
108
- handleRedirection();
109
- } else if (actionPosition === 'back') {
110
- const previousRelation = getPreviousDocument();
111
- if (previousRelation) {
112
- removeLastDocumentFromHistory();
113
- changeDocument(previousRelation);
114
- }
115
- } else {
116
- // Add current relation to history before opening a new one in case we are opening a new one
117
- if (currentDocumentMeta && Object.keys(currentDocumentMeta).length > 0) {
118
- addDocumentToHistory(currentDocumentMeta);
119
- }
120
- handleToggleModal();
311
+ if (state.confirmDialogIntent === null) {
312
+ return;
121
313
  }
122
- };
123
- return /*#__PURE__*/ jsx(Form, {
124
- method: "PUT",
125
- initialValues: currentDocument.getInitialFormValues(),
126
- validate: (values, options)=>{
127
- const yupSchema = createYupSchema(currentDocument.schema?.attributes, currentDocument.components, {
128
- status: currentDocument.document?.status,
129
- ...options
314
+ if (state.confirmDialogIntent === 'navigate') {
315
+ handleRedirection();
316
+ } else if (state.confirmDialogIntent === 'back') {
317
+ dispatch({
318
+ type: 'GO_BACK',
319
+ payload: {
320
+ shouldBypassConfirmation: true
321
+ }
130
322
  });
131
- return yupSchema.validate(values, {
132
- abortEarly: false
323
+ } else if (state.confirmDialogIntent === 'close') {
324
+ handleCloseModal(true);
325
+ } else if ('documentId' in state.confirmDialogIntent) {
326
+ dispatch({
327
+ type: 'GO_TO_RELATION',
328
+ payload: {
329
+ document: state.confirmDialogIntent,
330
+ shouldBypassConfirmation: true
331
+ }
133
332
  });
134
- },
135
- children: ({ modified, isSubmitting, resetForm })=>{
136
- // We don't count the root document, so history starts after 1
137
- const hasHistory = documentHistory.length > 1;
138
- return /*#__PURE__*/ jsxs(RelationModalProvider, {
139
- parentModified: modified,
140
- depth: depth,
141
- children: [
142
- /*#__PURE__*/ jsxs(Modal.Root, {
143
- open: isModalOpen,
144
- onOpenChange: ()=>{
145
- if (isModalOpen) {
146
- if (modified && !isSubmitting) {
147
- setIsConfirmationOpen(true);
148
- } else {
149
- handleToggleModal();
150
- }
151
- }
152
- },
153
- children: [
154
- /*#__PURE__*/ jsx(Modal.Trigger, {
155
- children: /*#__PURE__*/ jsx(Tooltip, {
156
- description: triggerButtonLabel,
157
- children: /*#__PURE__*/ jsx(CustomTextButton, {
158
- onClick: ()=>{
159
- // Check if parent modal has unsaved changes
160
- if (isNested && parentContext.parentModified) {
161
- setIsConfirmationOpen(true);
162
- // Return early to avoid opening the modal
163
- return;
164
- } else {
165
- if (modified && !isSubmitting) {
166
- setIsConfirmationOpen(true);
167
- } else {
168
- // Add current relation to history before opening a new one
169
- if (currentDocumentMeta && Object.keys(currentDocumentMeta).length > 0) {
170
- addDocumentToHistory(currentDocumentMeta);
171
- }
172
- handleToggleModal();
173
- }
174
- if (!isModalOpen) {
175
- setIsModalOpen(true);
176
- }
177
- }
178
- },
179
- width: "100%",
180
- children: triggerButtonLabel
181
- })
182
- })
183
- }),
184
- /*#__PURE__*/ jsxs(CustomModalContent, {
185
- children: [
186
- /*#__PURE__*/ jsx(Modal.Header, {
187
- gap: 2,
188
- children: /*#__PURE__*/ jsx(Flex, {
189
- justifyContent: "space-between",
190
- alignItems: "center",
191
- width: "100%",
192
- children: /*#__PURE__*/ jsxs(Flex, {
193
- gap: 2,
194
- children: [
195
- /*#__PURE__*/ jsx(IconButton, {
196
- withTooltip: false,
197
- label: "Back",
198
- variant: "ghost",
199
- disabled: !hasHistory,
200
- onClick: ()=>{
201
- setActionPosition('back');
202
- if (modified && !isSubmitting) {
203
- setIsConfirmationOpen(true);
204
- } else {
205
- const previousRelation = getPreviousDocument();
206
- if (previousRelation) {
207
- removeLastDocumentFromHistory();
208
- changeDocument(previousRelation);
209
- }
210
- }
211
- },
212
- marginRight: 1,
213
- children: /*#__PURE__*/ jsx(ArrowLeft, {})
214
- }),
215
- /*#__PURE__*/ jsx(Typography, {
216
- tag: "span",
217
- fontWeight: 600,
218
- children: formatMessage({
219
- id: 'content-manager.components.RelationInputModal.modal-title',
220
- defaultMessage: 'Edit a relation'
221
- })
222
- })
223
- ]
224
- })
225
- })
226
- }),
227
- /*#__PURE__*/ jsx(RelationModalBody, {
228
- children: /*#__PURE__*/ jsx(IconButton, {
229
- onClick: ()=>{
230
- setActionPosition('navigate');
231
- if (modified && !isSubmitting) {
232
- setIsConfirmationOpen(true);
233
- } else {
234
- navigate(getFullPageLink());
235
- }
236
- },
237
- variant: "tertiary",
238
- label: formatMessage({
239
- id: 'content-manager.components.RelationInputModal.button-fullpage',
240
- defaultMessage: 'Go to entry'
241
- }),
242
- children: /*#__PURE__*/ jsx(ArrowsOut, {})
243
- })
244
- }),
245
- /*#__PURE__*/ jsx(Modal.Footer, {
246
- children: /*#__PURE__*/ jsx(Button, {
247
- onClick: ()=>{
248
- if (modified && !isSubmitting) {
249
- setIsConfirmationOpen(true);
250
- } else {
251
- handleToggleModal();
252
- }
253
- },
254
- variant: "tertiary",
255
- children: formatMessage({
256
- id: 'app.components.Button.cancel',
257
- defaultMessage: 'Cancel'
258
- })
259
- })
260
- })
261
- ]
262
- })
263
- ]
264
- }),
265
- /*#__PURE__*/ jsx(Dialog.Root, {
266
- open: isConfirmationOpen,
267
- onOpenChange: setIsConfirmationOpen,
268
- children: /*#__PURE__*/ jsx(ConfirmDialog, {
269
- onConfirm: ()=>{
270
- handleConfirm();
271
- setIsConfirmationOpen(false);
272
- resetForm();
273
- },
274
- onCancel: ()=>{
275
- setIsConfirmationOpen(false);
276
- },
277
- variant: "danger",
278
- children: formatMessage({
279
- id: 'content-manager.components.RelationInputModal.confirmation-message',
280
- defaultMessage: 'Some changes were not saved. Are you sure you want to close this relation? All changes that were not saved will be lost.'
281
- })
282
- })
333
+ }
334
+ };
335
+ return /*#__PURE__*/ jsxs(Fragment, {
336
+ children: [
337
+ /*#__PURE__*/ jsx(RelationEditView, {}),
338
+ /*#__PURE__*/ jsx(Dialog.Root, {
339
+ open: state.confirmDialogIntent != null,
340
+ children: /*#__PURE__*/ jsx(ConfirmDialog, {
341
+ onConfirm: ()=>handleConfirm(),
342
+ onCancel: ()=>dispatch({
343
+ type: 'CANCEL_CONFIRM_DIALOG'
344
+ }),
345
+ variant: "danger",
346
+ children: formatMessage({
347
+ id: 'content-manager.components.RelationInputModal.confirmation-message',
348
+ defaultMessage: 'Some changes were not saved. Are you sure you want to close this relation? All changes that were not saved will be lost.'
283
349
  })
284
- ]
350
+ })
351
+ })
352
+ ]
353
+ });
354
+ };
355
+ const ModalTrigger = ({ children, relation })=>{
356
+ const dispatch = useRelationModal('ModalTrigger', (state)=>state.dispatch);
357
+ return /*#__PURE__*/ jsx(StyledTextButton, {
358
+ onClick: ()=>{
359
+ dispatch({
360
+ type: 'GO_TO_RELATION',
361
+ payload: {
362
+ document: relation,
363
+ shouldBypassConfirmation: false
364
+ }
285
365
  });
286
- }
366
+ },
367
+ children: children
287
368
  });
288
369
  };
289
- const CustomTextButton = styled(TextButton)`
370
+ const RelationModal = /*#__PURE__*/ React.memo(({ relation, children })=>{
371
+ return /*#__PURE__*/ jsx(RelationModalRenderer, {
372
+ relation: relation,
373
+ trigger: /*#__PURE__*/ jsx(ModalTrigger, {
374
+ relation: relation,
375
+ children: children
376
+ }),
377
+ children: /*#__PURE__*/ jsx(RelationModalBody, {})
378
+ });
379
+ });
380
+ const StyledTextButton = styled(TextButton)`
381
+ max-width: 100%;
290
382
  & > span {
291
383
  font-size: ${({ theme })=>theme.fontSizes[2]};
292
384
  width: inherit;
@@ -295,20 +387,22 @@ const CustomTextButton = styled(TextButton)`
295
387
  text-overflow: ellipsis;
296
388
  }
297
389
  `;
298
- const RelationModalBody = ({ children })=>{
390
+ /**
391
+ * The mini edit view for a relation that is displayed inside a modal.
392
+ * It's complete with its header, document actions and form layout.
393
+ */ const RelationEditView = ()=>{
299
394
  const { formatMessage } = useIntl();
300
- const documentMeta = useDocumentContext('RelationModalBody', (state)=>state.meta);
301
- const documentResponse = useDocumentContext('RelationModalBody', (state)=>state.document);
302
- const onPreview = useDocumentContext('RelationModalBody', (state)=>state.onPreview);
303
- const documentLayoutResponse = useDocumentLayout(documentMeta.model);
395
+ const currentDocumentMeta = useRelationModal('RelationModalBody', (state)=>state.currentDocumentMeta);
396
+ const currentDocument = useRelationModal('RelationModalBody', (state)=>state.currentDocument);
397
+ const documentLayoutResponse = useDocumentLayout(currentDocumentMeta.model);
304
398
  const plugins = useStrapiApp('RelationModalBody', (state)=>state.plugins);
305
- const initialValues = documentResponse.getInitialFormValues();
399
+ const initialValues = currentDocument.getInitialFormValues();
306
400
  const { permissions = [], isLoading: isLoadingPermissions, error } = useRBAC(PERMISSIONS.map((action)=>({
307
401
  action,
308
- subject: documentMeta.model
402
+ subject: currentDocumentMeta.model
309
403
  })));
310
- const isLoading = isLoadingPermissions || documentLayoutResponse.isLoading || documentResponse.isLoading;
311
- if (isLoading && !documentResponse.document?.documentId) {
404
+ const isLoading = isLoadingPermissions || documentLayoutResponse.isLoading || currentDocument.isLoading;
405
+ if (isLoading && !currentDocument.document?.documentId) {
312
406
  return /*#__PURE__*/ jsx(Loader, {
313
407
  small: true,
314
408
  children: formatMessage({
@@ -317,7 +411,7 @@ const RelationModalBody = ({ children })=>{
317
411
  })
318
412
  });
319
413
  }
320
- if (error || !documentMeta.model || documentLayoutResponse.error || !documentResponse.document || !documentResponse.meta || !documentResponse.schema || !initialValues) {
414
+ if (error || !currentDocumentMeta.model || documentLayoutResponse.error || !currentDocument.document || !currentDocument.meta || !currentDocument.schema || !initialValues) {
321
415
  return /*#__PURE__*/ jsx(Flex, {
322
416
  alignItems: "center",
323
417
  height: "100%",
@@ -333,106 +427,98 @@ const RelationModalBody = ({ children })=>{
333
427
  })
334
428
  });
335
429
  }
336
- const documentTitle = documentResponse.getTitle(documentLayoutResponse.edit.settings.mainField);
337
- const hasDraftAndPublished = documentResponse.schema?.options?.draftAndPublish ?? false;
430
+ const documentTitle = currentDocument.getTitle(documentLayoutResponse.edit.settings.mainField);
431
+ const hasDraftAndPublished = currentDocument.schema?.options?.draftAndPublish ?? false;
338
432
  const props = {
339
433
  activeTab: 'draft',
340
- collectionType: documentMeta.collectionType,
341
- model: documentMeta.model,
342
- documentId: documentMeta.documentId,
343
- document: documentResponse.document,
344
- meta: documentResponse.meta,
345
- onPreview,
346
- fromRelationModal: true,
347
- fromPreview: onPreview !== undefined
434
+ collectionType: currentDocumentMeta.collectionType,
435
+ model: currentDocumentMeta.model,
436
+ documentId: currentDocumentMeta.documentId,
437
+ document: currentDocument.document,
438
+ meta: currentDocument.meta
348
439
  };
349
- return /*#__PURE__*/ jsx(Modal.Body, {
350
- children: /*#__PURE__*/ jsxs(DocumentRBAC, {
351
- permissions: permissions,
352
- model: documentMeta.model,
353
- children: [
354
- /*#__PURE__*/ jsxs(Flex, {
355
- alignItems: "flex-start",
356
- direction: "column",
357
- gap: 2,
358
- children: [
359
- /*#__PURE__*/ jsxs(Flex, {
360
- width: "100%",
361
- justifyContent: "space-between",
362
- gap: 2,
363
- children: [
364
- /*#__PURE__*/ jsx(Typography, {
365
- tag: "h2",
366
- variant: "alpha",
367
- children: documentTitle
368
- }),
369
- /*#__PURE__*/ jsxs(Flex, {
370
- gap: 2,
371
- children: [
372
- children,
373
- /*#__PURE__*/ jsx(DescriptionComponentRenderer, {
374
- props: props,
375
- descriptions: plugins['content-manager'].apis.getDocumentActions('relation-modal'),
376
- children: (actions)=>{
377
- const filteredActions = actions.filter((action)=>{
378
- return [
379
- action.position
380
- ].flat().includes('relation-modal');
381
- });
382
- const [primaryAction, secondaryAction] = filteredActions;
383
- if (!primaryAction && !secondaryAction) return null;
384
- // Both actions are available when draft and publish enabled
385
- if (primaryAction && secondaryAction) {
386
- return /*#__PURE__*/ jsxs(Fragment, {
387
- children: [
388
- /*#__PURE__*/ jsx(DocumentActionButton, {
389
- ...secondaryAction,
390
- variant: secondaryAction.variant || 'secondary'
391
- }),
392
- /*#__PURE__*/ jsx(DocumentActionButton, {
393
- ...primaryAction,
394
- variant: primaryAction.variant || 'default'
395
- })
396
- ]
397
- });
398
- }
399
- // Otherwise we just have the save action
400
- return /*#__PURE__*/ jsx(DocumentActionButton, {
401
- ...primaryAction,
402
- variant: primaryAction.variant || 'secondary'
403
- });
404
- }
405
- })
406
- ]
440
+ return /*#__PURE__*/ jsxs(DocumentRBAC, {
441
+ permissions: permissions,
442
+ model: currentDocumentMeta.model,
443
+ children: [
444
+ /*#__PURE__*/ jsxs(Flex, {
445
+ alignItems: "flex-start",
446
+ direction: "column",
447
+ gap: 2,
448
+ children: [
449
+ /*#__PURE__*/ jsxs(Flex, {
450
+ width: "100%",
451
+ justifyContent: "space-between",
452
+ gap: 2,
453
+ children: [
454
+ /*#__PURE__*/ jsx(Typography, {
455
+ tag: "h2",
456
+ variant: "alpha",
457
+ children: documentTitle
458
+ }),
459
+ /*#__PURE__*/ jsx(Flex, {
460
+ gap: 2,
461
+ children: /*#__PURE__*/ jsx(DescriptionComponentRenderer, {
462
+ props: props,
463
+ descriptions: plugins['content-manager'].apis.getDocumentActions('relation-modal'),
464
+ children: (actions)=>{
465
+ const filteredActions = actions.filter((action)=>{
466
+ return [
467
+ action.position
468
+ ].flat().includes('relation-modal');
469
+ });
470
+ const [primaryAction, secondaryAction] = filteredActions;
471
+ if (!primaryAction && !secondaryAction) return null;
472
+ // Both actions are available when draft and publish enabled
473
+ if (primaryAction && secondaryAction) {
474
+ return /*#__PURE__*/ jsxs(Fragment, {
475
+ children: [
476
+ /*#__PURE__*/ jsx(DocumentActionButton, {
477
+ ...secondaryAction,
478
+ variant: secondaryAction.variant || 'secondary'
479
+ }),
480
+ /*#__PURE__*/ jsx(DocumentActionButton, {
481
+ ...primaryAction,
482
+ variant: primaryAction.variant || 'default'
483
+ })
484
+ ]
485
+ });
486
+ }
487
+ // Otherwise we just have the save action
488
+ return /*#__PURE__*/ jsx(DocumentActionButton, {
489
+ ...primaryAction,
490
+ variant: primaryAction.variant || 'secondary'
491
+ });
492
+ }
407
493
  })
408
- ]
409
- }),
410
- hasDraftAndPublished ? /*#__PURE__*/ jsx(Box, {
411
- children: /*#__PURE__*/ jsx(DocumentStatus, {
412
- status: documentResponse.document?.status
413
494
  })
414
- }) : null
415
- ]
416
- }),
417
- /*#__PURE__*/ jsx(Flex, {
418
- flex: 1,
419
- overflow: "auto",
420
- alignItems: "stretch",
421
- paddingTop: 7,
422
- children: /*#__PURE__*/ jsx(Box, {
423
- overflow: "auto",
424
- flex: 1,
425
- children: /*#__PURE__*/ jsx(FormLayout, {
426
- layout: documentLayoutResponse.edit.layout,
427
- document: documentResponse,
428
- hasBackground: false
495
+ ]
496
+ }),
497
+ hasDraftAndPublished ? /*#__PURE__*/ jsx(Box, {
498
+ children: /*#__PURE__*/ jsx(DocumentStatus, {
499
+ status: currentDocument.document?.status
429
500
  })
501
+ }) : null
502
+ ]
503
+ }),
504
+ /*#__PURE__*/ jsx(Flex, {
505
+ flex: 1,
506
+ overflow: "auto",
507
+ alignItems: "stretch",
508
+ paddingTop: 7,
509
+ children: /*#__PURE__*/ jsx(Box, {
510
+ overflow: "auto",
511
+ flex: 1,
512
+ children: /*#__PURE__*/ jsx(FormLayout, {
513
+ layout: documentLayoutResponse.edit.layout,
514
+ document: currentDocument,
515
+ hasBackground: false
430
516
  })
431
517
  })
432
- ]
433
- })
518
+ })
519
+ ]
434
520
  });
435
521
  };
436
522
 
437
- export { RelationModalForm, getCollectionType };
523
+ export { RelationModal, getCollectionType, reducer, useRelationModal };
438
524
  //# sourceMappingURL=RelationModal.mjs.map