@strapi/content-manager 5.11.1 → 5.12.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/admin/chunks/{ComponentConfigurationPage-BGBNQxFJ.js → ComponentConfigurationPage-CBvwLnAK.js} +5 -6
  2. package/dist/admin/chunks/{ComponentConfigurationPage-BGBNQxFJ.js.map → ComponentConfigurationPage-CBvwLnAK.js.map} +1 -1
  3. package/dist/admin/chunks/{ComponentConfigurationPage-DKtL-tec.mjs → ComponentConfigurationPage-w_ss7xEy.mjs} +5 -6
  4. package/dist/admin/chunks/{ComponentConfigurationPage-DKtL-tec.mjs.map → ComponentConfigurationPage-w_ss7xEy.mjs.map} +1 -1
  5. package/dist/admin/chunks/{EditConfigurationPage-yliOxLUv.js → EditConfigurationPage-CbAhYSyH.js} +5 -6
  6. package/dist/admin/chunks/{EditConfigurationPage-yliOxLUv.js.map → EditConfigurationPage-CbAhYSyH.js.map} +1 -1
  7. package/dist/admin/chunks/{EditConfigurationPage-KDwSFRyn.mjs → EditConfigurationPage-D1iHIzSR.mjs} +5 -6
  8. package/dist/admin/chunks/{EditConfigurationPage-KDwSFRyn.mjs.map → EditConfigurationPage-D1iHIzSR.mjs.map} +1 -1
  9. package/dist/admin/chunks/{EditViewPage-BAI2r49P.mjs → EditViewPage-BtunY0ZP.mjs} +102 -95
  10. package/dist/admin/chunks/EditViewPage-BtunY0ZP.mjs.map +1 -0
  11. package/dist/admin/chunks/{EditViewPage-DpvonssD.js → EditViewPage-Cf_TPJZd.js} +102 -95
  12. package/dist/admin/chunks/EditViewPage-Cf_TPJZd.js.map +1 -0
  13. package/dist/admin/chunks/{Form-BrzQyDxU.mjs → Form-DsvK3WTh.mjs} +3 -3
  14. package/dist/admin/chunks/{Form-BrzQyDxU.mjs.map → Form-DsvK3WTh.mjs.map} +1 -1
  15. package/dist/admin/chunks/{Form-XD_sf25E.js → Form-UMrizqJP.js} +5 -5
  16. package/dist/admin/chunks/{Form-XD_sf25E.js.map → Form-UMrizqJP.js.map} +1 -1
  17. package/dist/admin/chunks/{History-C-_o7tz8.js → History-Crvb_hzd.js} +17 -13
  18. package/dist/admin/chunks/History-Crvb_hzd.js.map +1 -0
  19. package/dist/admin/chunks/{History-DClLuGIg.mjs → History-DsRVSUF7.mjs} +16 -12
  20. package/dist/admin/chunks/History-DsRVSUF7.mjs.map +1 -0
  21. package/dist/admin/chunks/{Input-Bv-rqfYH.js → Input-C2r54bIL.js} +1327 -64
  22. package/dist/admin/chunks/Input-C2r54bIL.js.map +1 -0
  23. package/dist/admin/chunks/{Input-BMLRZBE3.mjs → Input-D6obstp0.mjs} +1315 -55
  24. package/dist/admin/chunks/Input-D6obstp0.mjs.map +1 -0
  25. package/dist/admin/chunks/{ListConfigurationPage-Do3UDres.mjs → ListConfigurationPage-DW-wAwmW.mjs} +4 -5
  26. package/dist/admin/chunks/{ListConfigurationPage-Do3UDres.mjs.map → ListConfigurationPage-DW-wAwmW.mjs.map} +1 -1
  27. package/dist/admin/chunks/{ListConfigurationPage-D66hgG4-.js → ListConfigurationPage-DlQ2pLyr.js} +6 -7
  28. package/dist/admin/chunks/{ListConfigurationPage-D66hgG4-.js.map → ListConfigurationPage-DlQ2pLyr.js.map} +1 -1
  29. package/dist/admin/chunks/{ListViewPage-Q0auz5lE.mjs → ListViewPage-DcUhPA9a.mjs} +3 -4
  30. package/dist/admin/chunks/{ListViewPage-Q0auz5lE.mjs.map → ListViewPage-DcUhPA9a.mjs.map} +1 -1
  31. package/dist/admin/chunks/{ListViewPage-DNvysJaJ.js → ListViewPage-nJJ227Fo.js} +7 -8
  32. package/dist/admin/chunks/{ListViewPage-DNvysJaJ.js.map → ListViewPage-nJJ227Fo.js.map} +1 -1
  33. package/dist/admin/chunks/{NoContentTypePage-C5qFFfgn.js → NoContentTypePage-Cxt-uFq7.js} +2 -2
  34. package/dist/admin/chunks/{NoContentTypePage-C5qFFfgn.js.map → NoContentTypePage-Cxt-uFq7.js.map} +1 -1
  35. package/dist/admin/chunks/{NoContentTypePage-B_oOeOQb.mjs → NoContentTypePage-DbRXR2cr.mjs} +2 -2
  36. package/dist/admin/chunks/{NoContentTypePage-B_oOeOQb.mjs.map → NoContentTypePage-DbRXR2cr.mjs.map} +1 -1
  37. package/dist/admin/chunks/{NoPermissionsPage-C9sDJXRu.js → NoPermissionsPage-BeMTv_SG.js} +2 -2
  38. package/dist/admin/chunks/{NoPermissionsPage-C9sDJXRu.js.map → NoPermissionsPage-BeMTv_SG.js.map} +1 -1
  39. package/dist/admin/chunks/{NoPermissionsPage-BAW7WY-M.mjs → NoPermissionsPage-RWPwNESA.mjs} +2 -2
  40. package/dist/admin/chunks/{NoPermissionsPage-BAW7WY-M.mjs.map → NoPermissionsPage-RWPwNESA.mjs.map} +1 -1
  41. package/dist/admin/chunks/{Preview-B6ThL2SA.js → Preview-CCjSV5Iu.js} +118 -200
  42. package/dist/admin/chunks/Preview-CCjSV5Iu.js.map +1 -0
  43. package/dist/admin/chunks/{Preview-05BZGpV2.mjs → Preview-aVLT3LM_.mjs} +121 -203
  44. package/dist/admin/chunks/Preview-aVLT3LM_.mjs.map +1 -0
  45. package/dist/admin/chunks/{en-CImiNxXE.mjs → en-C2zEwS3-.mjs} +4 -1
  46. package/dist/admin/chunks/{en-CImiNxXE.mjs.map → en-C2zEwS3-.mjs.map} +1 -1
  47. package/dist/admin/chunks/{en-CLf4SuMQ.js → en-G976DLsg.js} +4 -1
  48. package/dist/admin/chunks/{en-CLf4SuMQ.js.map → en-G976DLsg.js.map} +1 -1
  49. package/dist/admin/chunks/{index-SQ88CePz.js → index-BA_JLxKS.js} +171 -117
  50. package/dist/admin/chunks/index-BA_JLxKS.js.map +1 -0
  51. package/dist/admin/chunks/{index-CcJLBLNf.mjs → index-CIOT3ggy.mjs} +170 -119
  52. package/dist/admin/chunks/index-CIOT3ggy.mjs.map +1 -0
  53. package/dist/admin/chunks/{layout-xxDnIsHG.js → layout-C510xcd6.js} +86 -13
  54. package/dist/admin/chunks/layout-C510xcd6.js.map +1 -0
  55. package/dist/admin/chunks/{layout-4nCaNnTs.mjs → layout-DjT9cccU.mjs} +78 -5
  56. package/dist/admin/chunks/layout-DjT9cccU.mjs.map +1 -0
  57. package/dist/admin/chunks/{useDragAndDrop-gcqEJMnO.js → objects-BJTP843m.js} +73 -1
  58. package/dist/admin/chunks/objects-BJTP843m.js.map +1 -0
  59. package/dist/admin/chunks/{useDragAndDrop-HYwNDExe.mjs → objects-D2z-IJgu.mjs} +72 -2
  60. package/dist/admin/chunks/objects-D2z-IJgu.mjs.map +1 -0
  61. package/dist/admin/chunks/{relations-VlsO9KQZ.js → usePrev-BglKW7a4.js} +18 -2
  62. package/dist/admin/chunks/usePrev-BglKW7a4.js.map +1 -0
  63. package/dist/admin/chunks/{relations-D1R7vM_e.mjs → usePrev-DAWUYIPh.mjs} +18 -4
  64. package/dist/admin/chunks/usePrev-DAWUYIPh.mjs.map +1 -0
  65. package/dist/admin/index.js +1 -1
  66. package/dist/admin/index.mjs +1 -1
  67. package/dist/admin/src/features/DocumentContext.d.ts +53 -0
  68. package/dist/admin/src/features/DocumentRBAC.d.ts +3 -2
  69. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  70. package/dist/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.d.ts +8 -0
  71. package/dist/admin/src/pages/EditView/components/FormInputs/{Relations.d.ts → Relations/Relations.d.ts} +9 -4
  72. package/dist/admin/src/pages/EditView/components/FormLayout.d.ts +8 -3
  73. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +7 -4
  74. package/dist/admin/src/preview/components/PreviewHeader.d.ts +1 -2
  75. package/dist/admin/src/preview/pages/Preview.d.ts +0 -2
  76. package/package.json +5 -5
  77. package/dist/admin/chunks/EditViewPage-BAI2r49P.mjs.map +0 -1
  78. package/dist/admin/chunks/EditViewPage-DpvonssD.js.map +0 -1
  79. package/dist/admin/chunks/History-C-_o7tz8.js.map +0 -1
  80. package/dist/admin/chunks/History-DClLuGIg.mjs.map +0 -1
  81. package/dist/admin/chunks/Input-BMLRZBE3.mjs.map +0 -1
  82. package/dist/admin/chunks/Input-Bv-rqfYH.js.map +0 -1
  83. package/dist/admin/chunks/Preview-05BZGpV2.mjs.map +0 -1
  84. package/dist/admin/chunks/Preview-B6ThL2SA.js.map +0 -1
  85. package/dist/admin/chunks/Relations-CJ0GWuqq.js +0 -1318
  86. package/dist/admin/chunks/Relations-CJ0GWuqq.js.map +0 -1
  87. package/dist/admin/chunks/Relations-CiOfFNxW.mjs +0 -1291
  88. package/dist/admin/chunks/Relations-CiOfFNxW.mjs.map +0 -1
  89. package/dist/admin/chunks/index-CcJLBLNf.mjs.map +0 -1
  90. package/dist/admin/chunks/index-SQ88CePz.js.map +0 -1
  91. package/dist/admin/chunks/layout-4nCaNnTs.mjs.map +0 -1
  92. package/dist/admin/chunks/layout-xxDnIsHG.js.map +0 -1
  93. package/dist/admin/chunks/objects-C3EebVVe.js +0 -76
  94. package/dist/admin/chunks/objects-C3EebVVe.js.map +0 -1
  95. package/dist/admin/chunks/objects-wl73iEma.mjs +0 -73
  96. package/dist/admin/chunks/objects-wl73iEma.mjs.map +0 -1
  97. package/dist/admin/chunks/relations-D1R7vM_e.mjs.map +0 -1
  98. package/dist/admin/chunks/relations-VlsO9KQZ.js.map +0 -1
  99. package/dist/admin/chunks/useDragAndDrop-HYwNDExe.mjs.map +0 -1
  100. package/dist/admin/chunks/useDragAndDrop-gcqEJMnO.js.map +0 -1
  101. package/dist/admin/chunks/usePrev-Bjw2dhmq.mjs +0 -18
  102. package/dist/admin/chunks/usePrev-Bjw2dhmq.mjs.map +0 -1
  103. package/dist/admin/chunks/usePrev-DIYl-IAL.js +0 -21
  104. package/dist/admin/chunks/usePrev-DIYl-IAL.js.map +0 -1
  105. package/dist/admin/src/preview/components/PreviewContent.d.ts +0 -2
@@ -6,7 +6,7 @@ var strapiAdmin = require('@strapi/admin/strapi-admin');
6
6
  var designSystem = require('@strapi/design-system');
7
7
  var Icons = require('@strapi/icons');
8
8
  var reactIntl = require('react-intl');
9
- var index = require('./index-SQ88CePz.js');
9
+ var index = require('./index-BA_JLxKS.js');
10
10
  var styledComponents = require('styled-components');
11
11
  var slate = require('slate');
12
12
  var slateHistory = require('slate-history');
@@ -62,16 +62,15 @@ require('prismjs/components/prism-typescript');
62
62
  require('prismjs/components/prism-tsx');
63
63
  require('prismjs/components/prism-vbnet');
64
64
  require('prismjs/components/prism-yaml');
65
- var usePrev = require('./usePrev-DIYl-IAL.js');
66
- var useDragAndDrop = require('./useDragAndDrop-gcqEJMnO.js');
65
+ var usePrev = require('./usePrev-BglKW7a4.js');
66
+ var objects = require('./objects-BJTP843m.js');
67
67
  var Toolbar = require('@radix-ui/react-toolbar');
68
68
  var reactDndHtml5Backend = require('react-dnd-html5-backend');
69
69
  var reactRouterDom = require('react-router-dom');
70
- var objects = require('./objects-C3EebVVe.js');
71
- var Relations = require('./Relations-CJ0GWuqq.js');
72
70
  var pipe$1 = require('lodash/fp/pipe');
73
71
  var ComponentIcon = require('./ComponentIcon-C-EjOUPA.js');
74
- var relations = require('./relations-VlsO9KQZ.js');
72
+ var fractionalIndexing = require('fractional-indexing');
73
+ var reactWindow = require('react-window');
75
74
  var CodeMirror = require('codemirror5');
76
75
  var sanitizeHtml = require('sanitize-html');
77
76
  var highlight_js = require('highlight.js');
@@ -2428,8 +2427,8 @@ const DropPlaceholder = styledComponents.styled(designSystem.Box)`
2428
2427
 
2429
2428
  // Show drop placeholder 8px above or below the drop target
2430
2429
  ${({ dragDirection, theme, placeholderMargin })=>styledComponents.css`
2431
- top: ${dragDirection === useDragAndDrop.DIRECTIONS.UPWARD && `-${theme.spaces[placeholderMargin]}`};
2432
- bottom: ${dragDirection === useDragAndDrop.DIRECTIONS.DOWNWARD && `-${theme.spaces[placeholderMargin]}`};
2430
+ top: ${dragDirection === objects.DIRECTIONS.UPWARD && `-${theme.spaces[placeholderMargin]}`};
2431
+ bottom: ${dragDirection === objects.DIRECTIONS.DOWNWARD && `-${theme.spaces[placeholderMargin]}`};
2433
2432
  `}
2434
2433
  `;
2435
2434
  const DragItem = styledComponents.styled(designSystem.Flex)`
@@ -2513,8 +2512,8 @@ const DragAndDropElement = ({ children, index: index$1, setDragDirection, dragDi
2513
2512
  name1,
2514
2513
  setLiveText
2515
2514
  ]);
2516
- const [{ handlerId, isDragging, isOverDropTarget, direction }, blockRef, dropRef, dragRef] = useDragAndDrop.useDragAndDrop(!disabled, {
2517
- type: `${useDragAndDrop.ItemTypes.BLOCKS}_${name1}`,
2515
+ const [{ handlerId, isDragging, isOverDropTarget, direction }, blockRef, dropRef, dragRef] = objects.useDragAndDrop(!disabled, {
2516
+ type: `${objects.ItemTypes.BLOCKS}_${name1}`,
2518
2517
  index: index$1,
2519
2518
  item: {
2520
2519
  index: index$1,
@@ -3485,6 +3484,20 @@ const Initializer = ({ disabled, name, onClick })=>{
3485
3484
  });
3486
3485
  };
3487
3486
 
3487
+ /**
3488
+ * We use this component to wrap any individual component field in the Edit View,
3489
+ * this could be a component field in a dynamic zone, a component within a repeatable space,
3490
+ * or even nested components.
3491
+ *
3492
+ * We primarily need this to provide the component id to the components so that they can
3493
+ * correctly fetch their relations.
3494
+ */ const [ComponentProvider, useComponent] = strapiAdmin.createContext('ComponentContext', {
3495
+ id: undefined,
3496
+ level: -1,
3497
+ uid: undefined,
3498
+ type: undefined
3499
+ });
3500
+
3488
3501
  const AddComponentButton = ({ hasError, isDisabled, isOpen, children, onClick })=>{
3489
3502
  return /*#__PURE__*/ jsxRuntime.jsx(StyledButton, {
3490
3503
  type: "button",
@@ -3590,15 +3603,24 @@ const ComponentCategory = ({ category, components = [], variant = 'primary', onA
3590
3603
  const ResponsiveAccordionContent = styledComponents.styled(designSystem.Accordion.Content)`
3591
3604
  container-type: inline-size;
3592
3605
  `;
3593
- const Grid = styledComponents.styled(designSystem.Box)`
3594
- display: grid;
3595
- grid-template-columns: repeat(auto-fill, 100%);
3596
- grid-gap: ${({ theme })=>theme.spaces[1]};
3597
-
3598
- @container (min-width: ${()=>RESPONSIVE_CONTAINER_BREAKPOINTS.sm}) {
3599
- grid-template-columns: repeat(auto-fill, 14rem);
3600
- }
3601
- `;
3606
+ /**
3607
+ * TODO:
3608
+ * JSDOM cannot handle container queries.
3609
+ * This is a temporary workaround so that tests do not fail in the CI when jestdom throws an error
3610
+ * for failing to parse the stylesheet.
3611
+ */ const Grid = process.env.NODE_ENV !== 'test' ? styledComponents.styled(designSystem.Box)`
3612
+ display: grid;
3613
+ grid-template-columns: repeat(auto-fill, 100%);
3614
+ grid-gap: 4px;
3615
+
3616
+ @container (min-width: ${()=>RESPONSIVE_CONTAINER_BREAKPOINTS.sm}) {
3617
+ grid-template-columns: repeat(auto-fill, 14rem);
3618
+ }
3619
+ ` : styledComponents.styled(designSystem.Box)`
3620
+ display: grid;
3621
+ grid-template-columns: repeat(auto-fill, 100%);
3622
+ grid-gap: 4px;
3623
+ `;
3602
3624
  const ComponentBox = styledComponents.styled(designSystem.Flex)`
3603
3625
  color: ${({ theme })=>theme.colors.neutral600};
3604
3626
  cursor: pointer;
@@ -3663,7 +3685,11 @@ const ComponentPicker = ({ dynamicComponentsByCategory = {}, isOpen, onClickAddC
3663
3685
  const DynamicComponent = ({ componentUid, disabled, index: index$1, name, onRemoveComponentClick, onMoveComponent, onGrabItem, onDropItem, onCancel, dynamicComponentsByCategory = {}, onAddComponent, children })=>{
3664
3686
  const { formatMessage } = reactIntl.useIntl();
3665
3687
  const formValues = strapiAdmin.useForm('DynamicComponent', (state)=>state.values);
3666
- const { edit: { components } } = index.useDocLayout();
3688
+ const documentMeta = index.useDocumentContext('DynamicComponent', (state)=>state.meta);
3689
+ const rootDocumentMeta = index.useDocumentContext('DynamicComponent', (state)=>state.rootDocumentMeta);
3690
+ const isRootDocument = rootDocumentMeta.model === documentMeta.model;
3691
+ const { edit: { components } } = index.useDocumentLayout(isRootDocument ? documentMeta.model : rootDocumentMeta.model);
3692
+ const document = index.useDocumentContext('DynamicComponent', (state)=>state.document);
3667
3693
  const title = React__namespace.useMemo(()=>{
3668
3694
  const { mainField } = components[componentUid]?.settings ?? {
3669
3695
  mainField: 'id'
@@ -3693,8 +3719,8 @@ const DynamicComponent = ({ componentUid, disabled, index: index$1, name, onRemo
3693
3719
  componentUid,
3694
3720
  dynamicComponentsByCategory
3695
3721
  ]);
3696
- const [{ handlerId, isDragging, handleKeyDown }, boxRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop.useDragAndDrop(!disabled, {
3697
- type: `${useDragAndDrop.ItemTypes.DYNAMIC_ZONE}_${name}`,
3722
+ const [{ handlerId, isDragging, handleKeyDown }, boxRef, dropRef, dragRef, dragPreviewRef] = objects.useDragAndDrop(!disabled, {
3723
+ type: `${objects.ItemTypes.DYNAMIC_ZONE}_${name}`,
3698
3724
  index: index$1,
3699
3725
  item: {
3700
3726
  index: index$1,
@@ -3895,9 +3921,11 @@ const DynamicComponent = ({ componentUid, disabled, index: index$1, name, onRemo
3895
3921
  alignItems: "stretch",
3896
3922
  children: children ? children({
3897
3923
  ...fieldWithTranslatedLabel,
3924
+ document,
3898
3925
  name: fieldName
3899
3926
  }) : /*#__PURE__*/ jsxRuntime.jsx(MemoizedInputRenderer, {
3900
3927
  ...fieldWithTranslatedLabel,
3928
+ document: document,
3901
3929
  name: fieldName
3902
3930
  })
3903
3931
  }, fieldName);
@@ -4015,7 +4043,8 @@ const DynamicZone = ({ attribute, disabled: disabledProp, hint, label, labelActi
4015
4043
  const { max = Infinity, min = -Infinity } = attribute ?? {};
4016
4044
  const [addComponentIsOpen, setAddComponentIsOpen] = React__namespace.useState(false);
4017
4045
  const [liveText, setLiveText] = React__namespace.useState('');
4018
- const { components, isLoading } = index.useDoc();
4046
+ const document = index.useDocumentContext('DynamicZone', (state)=>state.document);
4047
+ const { components, isLoading } = document;
4019
4048
  const disabled = disabledProp || isLoading;
4020
4049
  const { addFieldRow, removeFieldRow, moveFieldRow } = strapiAdmin.useForm('DynamicZone', ({ addFieldRow, removeFieldRow, moveFieldRow })=>({
4021
4050
  addFieldRow,
@@ -4143,7 +4172,7 @@ const DynamicZone = ({ attribute, disabled: disabledProp, hint, label, labelActi
4143
4172
  componentName: label || name
4144
4173
  });
4145
4174
  };
4146
- const level = Relations.useComponent('DynamicZone', (state)=>state.level);
4175
+ const level = useComponent('DynamicZone', (state)=>state.level);
4147
4176
  const ariaDescriptionId = React__namespace.useId();
4148
4177
  return /*#__PURE__*/ jsxRuntime.jsx(DynamicZoneProvider, {
4149
4178
  isInDynamicZone: true,
@@ -4175,7 +4204,7 @@ const DynamicZone = ({ attribute, disabled: disabledProp, hint, label, labelActi
4175
4204
  }),
4176
4205
  /*#__PURE__*/ jsxRuntime.jsx("ol", {
4177
4206
  "aria-describedby": ariaDescriptionId,
4178
- children: value.map((field, index)=>/*#__PURE__*/ jsxRuntime.jsx(Relations.ComponentProvider, {
4207
+ children: value.map((field, index)=>/*#__PURE__*/ jsxRuntime.jsx(ComponentProvider, {
4179
4208
  level: level + 1,
4180
4209
  uid: field.__component,
4181
4210
  // id is always a number in a dynamic zone.
@@ -4248,6 +4277,1230 @@ const NotAllowedInput = ({ hint, label, required, name })=>{
4248
4277
  });
4249
4278
  };
4250
4279
 
4280
+ function getCollectionType(url) {
4281
+ const regex = new RegExp(`(${index.COLLECTION_TYPES}|${index.SINGLE_TYPES})`);
4282
+ const match = url.match(regex);
4283
+ return match ? match[1] : undefined;
4284
+ }
4285
+ const CustomModalContent = styledComponents.styled(designSystem.Modal.Content)`
4286
+ width: 90%;
4287
+ max-width: 100%;
4288
+ height: 90%;
4289
+ max-height: 100%;
4290
+ `;
4291
+ const [RelationModalProvider, useRelationModal] = strapiAdmin.createContext('RelationModal', {
4292
+ parentModified: false,
4293
+ depth: 0
4294
+ });
4295
+ const RelationModalForm = ({ relation, triggerButtonLabel })=>{
4296
+ const navigate = reactRouterDom.useNavigate();
4297
+ const { pathname, search } = reactRouterDom.useLocation();
4298
+ const { formatMessage } = reactIntl.useIntl();
4299
+ const [triggerRefetchDocument] = index.useLazyGetDocumentQuery();
4300
+ const currentDocument = index.useDocumentContext('RelationModalForm', (state)=>state.document);
4301
+ const rootDocumentMeta = index.useDocumentContext('RelationModalForm', (state)=>state.rootDocumentMeta);
4302
+ const currentDocumentMeta = index.useDocumentContext('RelationModalForm', (state)=>state.meta);
4303
+ const changeDocument = index.useDocumentContext('RelationModalForm', (state)=>state.changeDocument);
4304
+ const documentHistory = index.useDocumentContext('RelationModalForm', (state)=>state.documentHistory);
4305
+ const setDocumentHistory = index.useDocumentContext('RelationModalForm', (state)=>state.setDocumentHistory);
4306
+ const [isConfirmationOpen, setIsConfirmationOpen] = React__namespace.useState(false);
4307
+ const [actionPosition, setActionPosition] = React__namespace.useState('cancel');
4308
+ const [isModalOpen, setIsModalOpen] = React__namespace.useState(false);
4309
+ // NOTE: Not sure about this relation modal context, maybe we should move this to DocumentContext?
4310
+ // Get parent modal context if it exists
4311
+ const parentContext = useRelationModal('RelationModalForm', (state)=>state);
4312
+ // Get depth of nested modals
4313
+ const depth = parentContext ? parentContext.depth + 1 : 0;
4314
+ // Check if this is a nested modal
4315
+ const isNested = depth > 0;
4316
+ const addDocumentToHistory = (document)=>setDocumentHistory((prev)=>[
4317
+ ...prev,
4318
+ document
4319
+ ]);
4320
+ const getPreviousDocument = ()=>{
4321
+ if (documentHistory.length === 0) return undefined;
4322
+ const lastDocument = documentHistory[documentHistory.length - 1];
4323
+ return lastDocument;
4324
+ };
4325
+ const removeLastDocumentFromHistory = ()=>{
4326
+ setDocumentHistory((prev)=>[
4327
+ ...prev
4328
+ ].slice(0, prev.length - 1));
4329
+ };
4330
+ const handleToggleModal = ()=>{
4331
+ if (isModalOpen) {
4332
+ setIsModalOpen(false);
4333
+ const document = {
4334
+ collectionType: rootDocumentMeta.collectionType,
4335
+ model: rootDocumentMeta.model,
4336
+ documentId: rootDocumentMeta.documentId
4337
+ };
4338
+ // Change back to the root document
4339
+ changeDocument(document);
4340
+ // Reset the document history
4341
+ setDocumentHistory([]);
4342
+ // Reset action position
4343
+ setActionPosition('cancel');
4344
+ // Read from cache or refetch root document
4345
+ triggerRefetchDocument(document, // Favor the cache
4346
+ true);
4347
+ } else {
4348
+ changeDocument(relation);
4349
+ setIsModalOpen(true);
4350
+ }
4351
+ };
4352
+ const getFullPageLink = ()=>{
4353
+ const isSingleType = currentDocumentMeta.collectionType === index.SINGLE_TYPES;
4354
+ const queryParams = currentDocumentMeta.params?.locale ? `?plugins[i18n][locale]=${currentDocumentMeta.params.locale}` : '';
4355
+ return `/content-manager/${currentDocumentMeta.collectionType}/${currentDocumentMeta.model}${isSingleType ? '' : '/' + currentDocumentMeta.documentId}${queryParams}`;
4356
+ };
4357
+ const handleRedirection = ()=>{
4358
+ const editViewUrl = `${pathname}${search}`;
4359
+ const isRootDocumentUrl = editViewUrl.includes(getFullPageLink());
4360
+ if (isRootDocumentUrl) {
4361
+ handleToggleModal();
4362
+ } else {
4363
+ navigate(getFullPageLink());
4364
+ }
4365
+ };
4366
+ const handleConfirm = ()=>{
4367
+ if (actionPosition === 'navigate') {
4368
+ handleRedirection();
4369
+ } else if (actionPosition === 'back') {
4370
+ const previousRelation = getPreviousDocument();
4371
+ if (previousRelation) {
4372
+ removeLastDocumentFromHistory();
4373
+ changeDocument(previousRelation);
4374
+ }
4375
+ } else {
4376
+ handleToggleModal();
4377
+ }
4378
+ };
4379
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
4380
+ method: "PUT",
4381
+ initialValues: currentDocument.getInitialFormValues(),
4382
+ validate: (values, options)=>{
4383
+ const yupSchema = index.createYupSchema(currentDocument.schema?.attributes, currentDocument.components, {
4384
+ status: currentDocument.document?.status,
4385
+ ...options
4386
+ });
4387
+ return yupSchema.validate(values, {
4388
+ abortEarly: false
4389
+ });
4390
+ },
4391
+ children: ({ modified, isSubmitting, resetForm })=>{
4392
+ // We don't count the root document, so history starts after 1
4393
+ const hasHistory = documentHistory.length > 1;
4394
+ return /*#__PURE__*/ jsxRuntime.jsxs(RelationModalProvider, {
4395
+ parentModified: modified,
4396
+ depth: depth,
4397
+ children: [
4398
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Root, {
4399
+ open: isModalOpen,
4400
+ onOpenChange: ()=>{
4401
+ if (isModalOpen) {
4402
+ if (modified && !isSubmitting) {
4403
+ setIsConfirmationOpen(true);
4404
+ } else {
4405
+ handleToggleModal();
4406
+ }
4407
+ }
4408
+ },
4409
+ children: [
4410
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Trigger, {
4411
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
4412
+ description: triggerButtonLabel,
4413
+ children: /*#__PURE__*/ jsxRuntime.jsx(CustomTextButton, {
4414
+ onClick: ()=>{
4415
+ // Check if parent modal has unsaved changes
4416
+ if (isNested && parentContext.parentModified) {
4417
+ setIsConfirmationOpen(true);
4418
+ // Return early to avoid opening the modal
4419
+ return;
4420
+ } else {
4421
+ if (modified && !isSubmitting) {
4422
+ setIsConfirmationOpen(true);
4423
+ } else {
4424
+ // Add current relation to history before opening a new one
4425
+ if (currentDocumentMeta && Object.keys(currentDocumentMeta).length > 0) {
4426
+ addDocumentToHistory(currentDocumentMeta);
4427
+ }
4428
+ handleToggleModal();
4429
+ }
4430
+ if (!isModalOpen) {
4431
+ setIsModalOpen(true);
4432
+ }
4433
+ }
4434
+ },
4435
+ children: triggerButtonLabel
4436
+ })
4437
+ })
4438
+ }),
4439
+ /*#__PURE__*/ jsxRuntime.jsxs(CustomModalContent, {
4440
+ children: [
4441
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Header, {
4442
+ gap: 2,
4443
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
4444
+ justifyContent: "space-between",
4445
+ alignItems: "center",
4446
+ width: "100%",
4447
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
4448
+ gap: 2,
4449
+ children: [
4450
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
4451
+ withTooltip: false,
4452
+ label: "Back",
4453
+ variant: "ghost",
4454
+ disabled: !hasHistory,
4455
+ onClick: ()=>{
4456
+ setActionPosition('back');
4457
+ if (modified && !isSubmitting) {
4458
+ setIsConfirmationOpen(true);
4459
+ } else {
4460
+ const previousRelation = getPreviousDocument();
4461
+ if (previousRelation) {
4462
+ removeLastDocumentFromHistory();
4463
+ changeDocument(previousRelation);
4464
+ }
4465
+ }
4466
+ },
4467
+ marginRight: 1,
4468
+ children: /*#__PURE__*/ jsxRuntime.jsx(Icons.ArrowLeft, {})
4469
+ }),
4470
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
4471
+ tag: "span",
4472
+ fontWeight: 600,
4473
+ children: formatMessage({
4474
+ id: 'content-manager.components.RelationInputModal.modal-title',
4475
+ defaultMessage: 'Edit a relation'
4476
+ })
4477
+ })
4478
+ ]
4479
+ })
4480
+ })
4481
+ }),
4482
+ /*#__PURE__*/ jsxRuntime.jsx(RelationModalBody, {
4483
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
4484
+ onClick: ()=>{
4485
+ setActionPosition('navigate');
4486
+ if (modified && !isSubmitting) {
4487
+ setIsConfirmationOpen(true);
4488
+ } else {
4489
+ navigate(getFullPageLink());
4490
+ }
4491
+ },
4492
+ variant: "tertiary",
4493
+ label: formatMessage({
4494
+ id: 'content-manager.components.RelationInputModal.button-fullpage',
4495
+ defaultMessage: 'Go to entry'
4496
+ }),
4497
+ children: /*#__PURE__*/ jsxRuntime.jsx(Icons.ArrowsOut, {})
4498
+ })
4499
+ }),
4500
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Footer, {
4501
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
4502
+ onClick: ()=>{
4503
+ if (modified && !isSubmitting) {
4504
+ setIsConfirmationOpen(true);
4505
+ } else {
4506
+ handleToggleModal();
4507
+ }
4508
+ },
4509
+ variant: "tertiary",
4510
+ children: formatMessage({
4511
+ id: 'app.components.Button.cancel',
4512
+ defaultMessage: 'Cancel'
4513
+ })
4514
+ })
4515
+ })
4516
+ ]
4517
+ })
4518
+ ]
4519
+ }),
4520
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Root, {
4521
+ open: isConfirmationOpen,
4522
+ onOpenChange: setIsConfirmationOpen,
4523
+ children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.ConfirmDialog, {
4524
+ onConfirm: ()=>{
4525
+ handleConfirm();
4526
+ setIsConfirmationOpen(false);
4527
+ resetForm();
4528
+ },
4529
+ onCancel: ()=>{
4530
+ setIsConfirmationOpen(false);
4531
+ },
4532
+ variant: "danger",
4533
+ children: formatMessage({
4534
+ id: 'content-manager.components.RelationInputModal.confirmation-message',
4535
+ defaultMessage: 'Some changes were not saved. Are you sure you want to close this relation? All changes that were not saved will be lost.'
4536
+ })
4537
+ })
4538
+ })
4539
+ ]
4540
+ });
4541
+ }
4542
+ });
4543
+ };
4544
+ const CustomTextButton = styledComponents.styled(designSystem.TextButton)`
4545
+ & > span {
4546
+ font-size: ${({ theme })=>theme.fontSizes[2]};
4547
+ }
4548
+ `;
4549
+ const RelationModalBody = ({ children })=>{
4550
+ const { formatMessage } = reactIntl.useIntl();
4551
+ const documentMeta = index.useDocumentContext('RelationModalBody', (state)=>state.meta);
4552
+ const documentResponse = index.useDocumentContext('RelationModalBody', (state)=>state.document);
4553
+ const onPreview = index.useDocumentContext('RelationModalBody', (state)=>state.onPreview);
4554
+ const documentLayoutResponse = index.useDocumentLayout(documentMeta.model);
4555
+ const plugins = strapiAdmin.useStrapiApp('RelationModalBody', (state)=>state.plugins);
4556
+ const initialValues = documentResponse.getInitialFormValues();
4557
+ const { permissions = [], isLoading: isLoadingPermissions, error } = strapiAdmin.useRBAC(index.PERMISSIONS.map((action)=>({
4558
+ action,
4559
+ subject: documentMeta.model
4560
+ })));
4561
+ const isLoading = isLoadingPermissions || documentLayoutResponse.isLoading || documentResponse.isLoading;
4562
+ if (isLoading && !documentResponse.document?.documentId) {
4563
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Loader, {
4564
+ small: true,
4565
+ children: formatMessage({
4566
+ id: 'content-manager.ListViewTable.relation-loading',
4567
+ defaultMessage: 'Relations are loading'
4568
+ })
4569
+ });
4570
+ }
4571
+ if (error || !documentMeta.model || documentLayoutResponse.error || !documentResponse.document || !documentResponse.meta || !documentResponse.schema || !initialValues) {
4572
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
4573
+ alignItems: "center",
4574
+ height: "100%",
4575
+ justifyContent: "center",
4576
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.EmptyStateLayout, {
4577
+ icon: /*#__PURE__*/ jsxRuntime.jsx(Icons.WarningCircle, {
4578
+ width: "16rem"
4579
+ }),
4580
+ content: formatMessage({
4581
+ id: 'anErrorOccurred',
4582
+ defaultMessage: 'Whoops! Something went wrong. Please, try again.'
4583
+ })
4584
+ })
4585
+ });
4586
+ }
4587
+ const documentTitle = documentResponse.getTitle(documentLayoutResponse.edit.settings.mainField);
4588
+ const hasDraftAndPublished = documentResponse.schema?.options?.draftAndPublish ?? false;
4589
+ const props = {
4590
+ activeTab: 'draft',
4591
+ collectionType: documentMeta.collectionType,
4592
+ model: documentMeta.model,
4593
+ documentId: documentMeta.documentId,
4594
+ document: documentResponse.document,
4595
+ meta: documentResponse.meta,
4596
+ onPreview
4597
+ };
4598
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Body, {
4599
+ children: /*#__PURE__*/ jsxRuntime.jsxs(index.DocumentRBAC, {
4600
+ permissions: permissions,
4601
+ model: documentMeta.model,
4602
+ children: [
4603
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
4604
+ alignItems: "flex-start",
4605
+ direction: "column",
4606
+ gap: 2,
4607
+ children: [
4608
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
4609
+ width: "100%",
4610
+ justifyContent: "space-between",
4611
+ gap: 2,
4612
+ children: [
4613
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
4614
+ tag: "h2",
4615
+ variant: "alpha",
4616
+ children: documentTitle
4617
+ }),
4618
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
4619
+ gap: 2,
4620
+ children: [
4621
+ children,
4622
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.DescriptionComponentRenderer, {
4623
+ props: props,
4624
+ descriptions: plugins['content-manager'].apis.getDocumentActions('relation-modal'),
4625
+ children: (actions)=>{
4626
+ const filteredActions = actions.filter((action)=>{
4627
+ return [
4628
+ action.position
4629
+ ].flat().includes('relation-modal');
4630
+ });
4631
+ const [primaryAction, secondaryAction] = filteredActions;
4632
+ if (!primaryAction && !secondaryAction) return null;
4633
+ // Both actions are available when draft and publish enabled
4634
+ if (primaryAction && secondaryAction) {
4635
+ return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
4636
+ children: [
4637
+ /*#__PURE__*/ jsxRuntime.jsx(index.DocumentActionButton, {
4638
+ ...secondaryAction,
4639
+ variant: secondaryAction.variant || 'secondary'
4640
+ }),
4641
+ /*#__PURE__*/ jsxRuntime.jsx(index.DocumentActionButton, {
4642
+ ...primaryAction,
4643
+ variant: primaryAction.variant || 'default'
4644
+ })
4645
+ ]
4646
+ });
4647
+ }
4648
+ // Otherwise we just have the save action
4649
+ return /*#__PURE__*/ jsxRuntime.jsx(index.DocumentActionButton, {
4650
+ ...primaryAction,
4651
+ variant: primaryAction.variant || 'secondary'
4652
+ });
4653
+ }
4654
+ })
4655
+ ]
4656
+ })
4657
+ ]
4658
+ }),
4659
+ hasDraftAndPublished ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
4660
+ children: /*#__PURE__*/ jsxRuntime.jsx(index.DocumentStatus, {
4661
+ status: documentResponse.document?.status
4662
+ })
4663
+ }) : null
4664
+ ]
4665
+ }),
4666
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
4667
+ flex: 1,
4668
+ overflow: "auto",
4669
+ alignItems: "stretch",
4670
+ paddingTop: 7,
4671
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
4672
+ overflow: "auto",
4673
+ flex: 1,
4674
+ children: /*#__PURE__*/ jsxRuntime.jsx(FormLayout, {
4675
+ layout: documentLayoutResponse.edit.layout,
4676
+ document: documentResponse,
4677
+ hasBackground: false
4678
+ })
4679
+ })
4680
+ })
4681
+ ]
4682
+ })
4683
+ });
4684
+ };
4685
+
4686
+ /**
4687
+ * Remove a relation, whether it's been already saved or not.
4688
+ * It's used both in RelationsList, where the "remove relation" button is, and in the input,
4689
+ * because we sometimes need to remove a previous relation when selecting a new one.
4690
+ */ function useHandleDisconnect(fieldName, consumerName) {
4691
+ const field = strapiAdmin.useField(fieldName);
4692
+ const removeFieldRow = strapiAdmin.useForm(consumerName, (state)=>state.removeFieldRow);
4693
+ const addFieldRow = strapiAdmin.useForm(consumerName, (state)=>state.addFieldRow);
4694
+ const handleDisconnect = (relation)=>{
4695
+ if (field.value && field.value.connect) {
4696
+ /**
4697
+ * A relation will exist in the `connect` array _if_ it has
4698
+ * been added without saving. In this case, we just remove it
4699
+ * from the connect array
4700
+ */ const indexOfRelationInConnectArray = field.value.connect.findIndex((rel)=>rel.id === relation.id);
4701
+ if (indexOfRelationInConnectArray >= 0) {
4702
+ removeFieldRow(`${fieldName}.connect`, indexOfRelationInConnectArray);
4703
+ return;
4704
+ }
4705
+ }
4706
+ addFieldRow(`${fieldName}.disconnect`, {
4707
+ id: relation.id,
4708
+ apiData: {
4709
+ id: relation.id,
4710
+ documentId: relation.documentId,
4711
+ locale: relation.locale
4712
+ }
4713
+ });
4714
+ };
4715
+ return handleDisconnect;
4716
+ }
4717
+ /* -------------------------------------------------------------------------------------------------
4718
+ * RelationsField
4719
+ * -----------------------------------------------------------------------------------------------*/ const RELATIONS_TO_DISPLAY = 5;
4720
+ const ONE_WAY_RELATIONS = [
4721
+ 'oneWay',
4722
+ 'oneToOne',
4723
+ 'manyToOne',
4724
+ 'oneToManyMorph',
4725
+ 'oneToOneMorph'
4726
+ ];
4727
+ /**
4728
+ * TODO: we get a rather ugly flash when we remove a single relation from the list leaving
4729
+ * no other relations when we press save. The initial relation re-renders, probably because
4730
+ * of the lag in the Form cleaning it's "disconnect" array, whilst our data has not been invalidated.
4731
+ *
4732
+ * Could we invalidate relation data on the document actions? Should we?
4733
+ */ /**
4734
+ * @internal
4735
+ * @description The relations field holds a lot of domain logic for handling relations which is rather complicated
4736
+ * At present we do not expose this to plugin developers, however, they are able to overwrite it themselves should
4737
+ * they wish to do so.
4738
+ */ const RelationsField = /*#__PURE__*/ React__namespace.forwardRef(({ disabled, label, ...props }, ref)=>{
4739
+ const currentDocumentMeta = index.useDocumentContext('RelationsField', (state)=>state.meta);
4740
+ const currentDocument = index.useDocumentContext('RelationsField', (state)=>state.document);
4741
+ const rootDocumentMeta = index.useDocumentContext('RelationsField', (state)=>state.rootDocumentMeta);
4742
+ const [currentPage, setCurrentPage] = React__namespace.useState(1);
4743
+ const isRootDocument = rootDocumentMeta.documentId === currentDocumentMeta.documentId;
4744
+ const documentMeta = isRootDocument ? rootDocumentMeta : currentDocumentMeta;
4745
+ // Use the documentId from the actual document, not the params (meta)
4746
+ const documentId = currentDocument.document?.documentId;
4747
+ const { formatMessage } = reactIntl.useIntl();
4748
+ const [{ query }] = strapiAdmin.useQueryParams();
4749
+ const params = documentMeta.params ?? index.buildValidParams(query);
4750
+ const isMorph = props.attribute.relation.toLowerCase().includes('morph');
4751
+ const isDisabled = isMorph || disabled;
4752
+ const { componentId, componentUID } = useComponent('RelationsField', ({ uid, id })=>({
4753
+ componentId: id,
4754
+ componentUID: uid
4755
+ }));
4756
+ const isSubmitting = strapiAdmin.useForm('RelationsList', (state)=>state.isSubmitting);
4757
+ React__namespace.useEffect(()=>{
4758
+ setCurrentPage(1);
4759
+ }, [
4760
+ isSubmitting
4761
+ ]);
4762
+ const component = componentUID && currentDocument.components[componentUID];
4763
+ /**
4764
+ * We'll always have a documentId in a created entry, so we look for a componentId first.
4765
+ * Same with `uid` and `documentModel`.
4766
+ */ const model = component ? component.uid : documentMeta.model;
4767
+ const id = component && componentId ? componentId.toString() : documentId;
4768
+ /**
4769
+ * The `name` prop is a complete path to the field, e.g. `field1.field2.field3`.
4770
+ * Where the above example would a nested field within two components, however
4771
+ * we only require the field on the component not the complete path since we query
4772
+ * individual components. Therefore we split the string and take the last item.
4773
+ */ const [targetField] = props.name.split('.').slice(-1);
4774
+ const schemaAttributes = component ? component.attributes ?? {} : currentDocument.schema?.attributes ?? {};
4775
+ /**
4776
+ * Confirm the target field is related to the current document.
4777
+ * Since relations can exist in a modal on top of the root document,
4778
+ * we need to ensure we are fetching relations for the correct document (root document vs related document),
4779
+ */ const isRelatedToCurrentDocument = Object.values(schemaAttributes).filter((attribute)=>attribute.type === 'relation' && 'target' in attribute && 'target' in props.attribute && attribute.target === props.attribute.target).length > 0;
4780
+ const { data, isLoading, isFetching } = usePrev.useGetRelationsQuery({
4781
+ model,
4782
+ targetField,
4783
+ // below we don't run the query if there is no id.
4784
+ id,
4785
+ params: {
4786
+ ...params,
4787
+ pageSize: RELATIONS_TO_DISPLAY,
4788
+ page: currentPage
4789
+ }
4790
+ }, {
4791
+ refetchOnMountOrArgChange: true,
4792
+ skip: !id || !isRelatedToCurrentDocument,
4793
+ selectFromResult: (result)=>{
4794
+ return {
4795
+ ...result,
4796
+ data: {
4797
+ ...result.data,
4798
+ results: result.data?.results ? result.data.results : []
4799
+ }
4800
+ };
4801
+ }
4802
+ });
4803
+ const handleLoadMore = ()=>{
4804
+ setCurrentPage((prev)=>prev + 1);
4805
+ };
4806
+ const field = strapiAdmin.useField(props.name);
4807
+ const isFetchingMoreRelations = isLoading || isFetching;
4808
+ const realServerRelationsCount = 'pagination' in data && data.pagination ? data.pagination.total : 0;
4809
+ /**
4810
+ * Items that are already connected, but reordered would be in
4811
+ * this list, so to get an accurate figure, we remove them.
4812
+ */ const relationsConnected = (field.value?.connect ?? []).filter((rel)=>data.results.findIndex((relation)=>relation.id === rel.id) === -1).length ?? 0;
4813
+ const relationsDisconnected = field.value?.disconnect?.length ?? 0;
4814
+ const relationsCount = realServerRelationsCount + relationsConnected - relationsDisconnected;
4815
+ /**
4816
+ * This is it, the source of truth for reordering in conjunction with partial loading & updating
4817
+ * of relations. Relations on load are given __temp_key__ when fetched, because we don't want to
4818
+ * create brand new keys everytime the data updates, just keep adding them onto the newly loaded ones.
4819
+ */ const relations = React__namespace.useMemo(()=>{
4820
+ const ctx = {
4821
+ field: field.value,
4822
+ // @ts-expect-error – targetModel does exist on the attribute. But it's not typed.
4823
+ href: `../${index.COLLECTION_TYPES}/${props.attribute.targetModel}`,
4824
+ mainField: props.mainField
4825
+ };
4826
+ /**
4827
+ * Tidy up our data.
4828
+ */ const transformations = pipe$1(removeConnected(ctx), removeDisconnected(ctx), addLabelAndHref(ctx));
4829
+ const transformedRels = transformations([
4830
+ ...data.results
4831
+ ]);
4832
+ /**
4833
+ * THIS IS CRUCIAL. If you don't sort by the __temp_key__ which comes from fractional indexing
4834
+ * then the list will be in the wrong order.
4835
+ */ return [
4836
+ ...transformedRels,
4837
+ ...field.value?.connect ?? []
4838
+ ].sort((a, b)=>{
4839
+ if (a.__temp_key__ < b.__temp_key__) return -1;
4840
+ if (a.__temp_key__ > b.__temp_key__) return 1;
4841
+ return 0;
4842
+ });
4843
+ }, [
4844
+ data.results,
4845
+ field.value,
4846
+ // @ts-expect-error – targetModel does exist on the attribute. But it's not typed.
4847
+ props.attribute.targetModel,
4848
+ props.mainField
4849
+ ]);
4850
+ const handleDisconnect = useHandleDisconnect(props.name, 'RelationsField');
4851
+ const handleConnect = (relation)=>{
4852
+ const [lastItemInList] = relations.slice(-1);
4853
+ const item = {
4854
+ id: relation.id,
4855
+ apiData: {
4856
+ id: relation.id,
4857
+ documentId: relation.documentId,
4858
+ locale: relation.locale
4859
+ },
4860
+ status: relation.status,
4861
+ /**
4862
+ * If there's a last item, that's the first key we use to generate out next one.
4863
+ */ __temp_key__: fractionalIndexing.generateNKeysBetween(lastItemInList?.__temp_key__ ?? null, null, 1)[0],
4864
+ // Fallback to `id` if there is no `mainField` value, which will overwrite the above `id` property with the exact same data.
4865
+ [props.mainField?.name ?? 'documentId']: relation[props.mainField?.name ?? 'documentId'],
4866
+ label: usePrev.getRelationLabel(relation, props.mainField),
4867
+ // @ts-expect-error – targetModel does exist on the attribute, but it's not typed.
4868
+ href: `../${index.COLLECTION_TYPES}/${props.attribute.targetModel}/${relation.documentId}?${relation.locale ? `plugins[i18n][locale]=${relation.locale}` : ''}`
4869
+ };
4870
+ if (ONE_WAY_RELATIONS.includes(props.attribute.relation)) {
4871
+ // Remove any existing relation so they can be replaced with the new one
4872
+ field.value?.connect?.forEach(handleDisconnect);
4873
+ relations.forEach(handleDisconnect);
4874
+ field.onChange(`${props.name}.connect`, [
4875
+ item
4876
+ ]);
4877
+ } else {
4878
+ field.onChange(`${props.name}.connect`, [
4879
+ ...field.value?.connect ?? [],
4880
+ item
4881
+ ]);
4882
+ }
4883
+ };
4884
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
4885
+ ref: ref,
4886
+ direction: "column",
4887
+ gap: 3,
4888
+ justifyContent: "space-between",
4889
+ alignItems: "stretch",
4890
+ wrap: "wrap",
4891
+ children: [
4892
+ /*#__PURE__*/ jsxRuntime.jsxs(StyledFlex, {
4893
+ direction: "column",
4894
+ alignItems: "start",
4895
+ gap: 2,
4896
+ width: "100%",
4897
+ children: [
4898
+ /*#__PURE__*/ jsxRuntime.jsx(RelationsInput, {
4899
+ disabled: isDisabled,
4900
+ // NOTE: we should not default to using the documentId if the component is being created (componentUID is undefined)
4901
+ id: componentUID && component ? componentId ? `${componentId}` : '' : documentId,
4902
+ label: `${label} ${relationsCount > 0 ? `(${relationsCount})` : ''}`,
4903
+ model: model,
4904
+ onChange: handleConnect,
4905
+ isRelatedToCurrentDocument: isRelatedToCurrentDocument,
4906
+ ...props
4907
+ }),
4908
+ 'pagination' in data && data.pagination && data.pagination.pageCount > data.pagination.page ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.TextButton, {
4909
+ disabled: isFetchingMoreRelations,
4910
+ onClick: handleLoadMore,
4911
+ loading: isFetchingMoreRelations,
4912
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icons.ArrowClockwise, {}),
4913
+ // prevent the label from line-wrapping
4914
+ shrink: 0,
4915
+ children: formatMessage({
4916
+ id: index.getTranslation('relation.loadMore'),
4917
+ defaultMessage: 'Load More'
4918
+ })
4919
+ }) : null
4920
+ ]
4921
+ }),
4922
+ /*#__PURE__*/ jsxRuntime.jsx(RelationsList, {
4923
+ data: relations,
4924
+ serverData: data.results,
4925
+ disabled: isDisabled,
4926
+ name: props.name,
4927
+ isLoading: isFetchingMoreRelations,
4928
+ relationType: props.attribute.relation,
4929
+ // @ts-expect-error – targetModel does exist on the attribute. But it's not typed.
4930
+ targetModel: props.attribute.targetModel
4931
+ })
4932
+ ]
4933
+ });
4934
+ });
4935
+ /**
4936
+ * TODO: this can be removed once we stop shipping Inputs with
4937
+ * labels wrapped round in DS@2.
4938
+ */ const StyledFlex = styledComponents.styled(designSystem.Flex)`
4939
+ & > div {
4940
+ width: 100%;
4941
+ }
4942
+ `;
4943
+ /**
4944
+ * If it's in the connected array, it can get out of our data array,
4945
+ * we'll be putting it back in later and sorting it anyway.
4946
+ */ const removeConnected = ({ field })=>(relations)=>{
4947
+ return relations.filter((relation)=>{
4948
+ const connectedRelations = field?.connect ?? [];
4949
+ return connectedRelations.findIndex((rel)=>rel.id === relation.id) === -1;
4950
+ });
4951
+ };
4952
+ /**
4953
+ * @description Removes relations that are in the `disconnect` array of the field
4954
+ */ const removeDisconnected = ({ field })=>(relations)=>relations.filter((relation)=>{
4955
+ const disconnectedRelations = field?.disconnect ?? [];
4956
+ return disconnectedRelations.findIndex((rel)=>rel.id === relation.id) === -1;
4957
+ });
4958
+ /**
4959
+ * @description Adds a label and href to the relation object we use this to render
4960
+ * a better UI where we can link to the relation and display a human-readable label.
4961
+ */ const addLabelAndHref = ({ mainField, href })=>(relations)=>relations.map((relation)=>{
4962
+ return {
4963
+ ...relation,
4964
+ // Fallback to `id` if there is no `mainField` value, which will overwrite the above `documentId` property with the exact same data.
4965
+ [mainField?.name ?? 'documentId']: relation[mainField?.name ?? 'documentId'],
4966
+ label: usePrev.getRelationLabel(relation, mainField),
4967
+ href: `${href}/${relation.documentId}?${relation.locale ? `plugins[i18n][locale]=${relation.locale}` : ''}`
4968
+ };
4969
+ });
4970
+ /**
4971
+ * @description Contains all the logic for the combobox that can search
4972
+ * for relations and then add them to the field's connect array.
4973
+ */ const RelationsInput = ({ hint, id, model, label, labelAction, name, mainField, placeholder, required, unique: _unique, 'aria-label': _ariaLabel, onChange, isRelatedToCurrentDocument, ...props })=>{
4974
+ const [textValue, setTextValue] = React__namespace.useState('');
4975
+ const [searchParams, setSearchParams] = React__namespace.useState({
4976
+ _q: '',
4977
+ page: 1
4978
+ });
4979
+ const { toggleNotification } = strapiAdmin.useNotification();
4980
+ const [{ query }] = strapiAdmin.useQueryParams();
4981
+ const currentDocumentMeta = index.useDocumentContext('RelationsInput', (state)=>state.meta);
4982
+ const rootDocumentMeta = index.useDocumentContext('RelationsInput', (state)=>state.rootDocumentMeta);
4983
+ const isRootDocument = rootDocumentMeta.documentId === currentDocumentMeta.documentId;
4984
+ const documentMeta = isRootDocument ? rootDocumentMeta : currentDocumentMeta;
4985
+ const { formatMessage } = reactIntl.useIntl();
4986
+ const fieldRef = strapiAdmin.useFocusInputField(name);
4987
+ const field = strapiAdmin.useField(name);
4988
+ const searchParamsDebounced = usePrev.useDebounce(searchParams, 300);
4989
+ const [searchForTrigger, { data, isLoading }] = usePrev.useLazySearchRelationsQuery();
4990
+ /**
4991
+ * Because we're using a lazy query, we need to trigger the search
4992
+ * when the component mounts and when the search params change.
4993
+ * We also need to trigger the search when the field value changes
4994
+ * so that we can filter out the relations that are already connected.
4995
+ */ React__namespace.useEffect(()=>{
4996
+ /**
4997
+ * The `name` prop is a complete path to the field, e.g. `field1.field2.field3`.
4998
+ * Where the above example would a nested field within two components, however
4999
+ * we only require the field on the component not the complete path since we query
5000
+ * individual components. Therefore we split the string and take the last item.
5001
+ */ const [targetField] = name.split('.').slice(-1);
5002
+ // Return early if there is no relation to the document
5003
+ if (!isRelatedToCurrentDocument) return;
5004
+ const params = documentMeta.params ?? index.buildValidParams(query);
5005
+ searchForTrigger({
5006
+ model,
5007
+ targetField,
5008
+ params: {
5009
+ ...params,
5010
+ id: id ?? '',
5011
+ pageSize: 10,
5012
+ idsToInclude: field.value?.disconnect?.map((rel)=>rel.id.toString()) ?? [],
5013
+ idsToOmit: field.value?.connect?.map((rel)=>rel.id.toString()) ?? [],
5014
+ ...searchParamsDebounced
5015
+ }
5016
+ });
5017
+ }, [
5018
+ field.value?.connect,
5019
+ field.value?.disconnect,
5020
+ id,
5021
+ model,
5022
+ name,
5023
+ query,
5024
+ searchForTrigger,
5025
+ searchParamsDebounced,
5026
+ isRelatedToCurrentDocument,
5027
+ documentMeta
5028
+ ]);
5029
+ const handleSearch = async (search)=>{
5030
+ setSearchParams((s)=>({
5031
+ ...s,
5032
+ _q: search,
5033
+ page: 1
5034
+ }));
5035
+ };
5036
+ const hasNextPage = data?.pagination ? data.pagination.page < data.pagination.pageCount : false;
5037
+ const options = data?.results ?? [];
5038
+ const handleChange = (relationId)=>{
5039
+ if (!relationId) {
5040
+ return;
5041
+ }
5042
+ const relation = options.find((opt)=>opt.id.toString() === relationId);
5043
+ if (!relation) {
5044
+ // This is very unlikely to happen, but it ensures we don't have any data for.
5045
+ console.error("You've tried to add a relation with an id that does not exist in the options you can see, this is likely a bug with Strapi. Please open an issue.");
5046
+ toggleNotification({
5047
+ message: formatMessage({
5048
+ id: index.getTranslation('relation.error-adding-relation'),
5049
+ defaultMessage: 'An error occurred while trying to add the relation.'
5050
+ }),
5051
+ type: 'danger'
5052
+ });
5053
+ return;
5054
+ }
5055
+ /**
5056
+ * You need to give this relation a correct _temp_key_ but
5057
+ * this component doesn't know about those ones, you can't rely
5058
+ * on the connect array because that doesn't hold items that haven't
5059
+ * moved. So use a callback to fill in the gaps when connecting.
5060
+ *
5061
+ */ onChange(relation);
5062
+ };
5063
+ const handleLoadMore = ()=>{
5064
+ if (!data || !data.pagination) {
5065
+ return;
5066
+ } else if (data.pagination.page < data.pagination.pageCount) {
5067
+ setSearchParams((s)=>({
5068
+ ...s,
5069
+ page: s.page + 1
5070
+ }));
5071
+ }
5072
+ };
5073
+ React__namespace.useLayoutEffect(()=>{
5074
+ setTextValue('');
5075
+ }, [
5076
+ field.value
5077
+ ]);
5078
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
5079
+ error: field.error,
5080
+ hint: hint,
5081
+ name: name,
5082
+ required: required,
5083
+ children: [
5084
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
5085
+ action: labelAction,
5086
+ children: label
5087
+ }),
5088
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Combobox, {
5089
+ ref: fieldRef,
5090
+ name: name,
5091
+ autocomplete: "list",
5092
+ placeholder: placeholder || formatMessage({
5093
+ id: index.getTranslation('relation.add'),
5094
+ defaultMessage: 'Add relation'
5095
+ }),
5096
+ hasMoreItems: hasNextPage,
5097
+ loading: isLoading,
5098
+ onOpenChange: ()=>{
5099
+ handleSearch(textValue ?? '');
5100
+ },
5101
+ noOptionsMessage: ()=>formatMessage({
5102
+ id: index.getTranslation('relation.notAvailable'),
5103
+ defaultMessage: 'No relations available'
5104
+ }),
5105
+ loadingMessage: formatMessage({
5106
+ id: index.getTranslation('relation.isLoading'),
5107
+ defaultMessage: 'Relations are loading'
5108
+ }),
5109
+ onLoadMore: handleLoadMore,
5110
+ textValue: textValue,
5111
+ onChange: handleChange,
5112
+ onTextValueChange: (text)=>{
5113
+ setTextValue(text);
5114
+ },
5115
+ onInputChange: (event)=>{
5116
+ handleSearch(event.currentTarget.value);
5117
+ },
5118
+ ...props,
5119
+ children: options.map((opt)=>{
5120
+ const textValue = usePrev.getRelationLabel(opt, mainField);
5121
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
5122
+ value: opt.id.toString(),
5123
+ textValue: textValue,
5124
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
5125
+ gap: 2,
5126
+ justifyContent: "space-between",
5127
+ children: [
5128
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
5129
+ ellipsis: true,
5130
+ children: textValue
5131
+ }),
5132
+ opt.status ? /*#__PURE__*/ jsxRuntime.jsx(index.DocumentStatus, {
5133
+ status: opt.status
5134
+ }) : null
5135
+ ]
5136
+ })
5137
+ }, opt.id);
5138
+ })
5139
+ }),
5140
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {}),
5141
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Hint, {})
5142
+ ]
5143
+ });
5144
+ };
5145
+ /* -------------------------------------------------------------------------------------------------
5146
+ * RelationsList
5147
+ * -----------------------------------------------------------------------------------------------*/ const RELATION_ITEM_HEIGHT = 50;
5148
+ const RELATION_GUTTER = 4;
5149
+ const RelationsList = ({ data, serverData, disabled, name, isLoading, relationType, targetModel })=>{
5150
+ const ariaDescriptionId = React__namespace.useId();
5151
+ const { formatMessage } = reactIntl.useIntl();
5152
+ const listRef = React__namespace.useRef(null);
5153
+ const outerListRef = React__namespace.useRef(null);
5154
+ const [overflow, setOverflow] = React__namespace.useState();
5155
+ const [liveText, setLiveText] = React__namespace.useState('');
5156
+ const field = strapiAdmin.useField(name);
5157
+ React__namespace.useEffect(()=>{
5158
+ if (data.length <= RELATIONS_TO_DISPLAY) {
5159
+ return setOverflow(undefined);
5160
+ }
5161
+ const handleNativeScroll = (e)=>{
5162
+ const el = e.target;
5163
+ const parentScrollContainerHeight = el.parentNode.scrollHeight;
5164
+ const maxScrollBottom = el.scrollHeight - el.scrollTop;
5165
+ if (el.scrollTop === 0) {
5166
+ return setOverflow('bottom');
5167
+ }
5168
+ if (maxScrollBottom === parentScrollContainerHeight) {
5169
+ return setOverflow('top');
5170
+ }
5171
+ return setOverflow('top-bottom');
5172
+ };
5173
+ const outerListRefCurrent = outerListRef?.current;
5174
+ if (!isLoading && data.length > 0 && outerListRefCurrent) {
5175
+ outerListRef.current.addEventListener('scroll', handleNativeScroll);
5176
+ }
5177
+ return ()=>{
5178
+ if (outerListRefCurrent) {
5179
+ outerListRefCurrent.removeEventListener('scroll', handleNativeScroll);
5180
+ }
5181
+ };
5182
+ }, [
5183
+ isLoading,
5184
+ data.length
5185
+ ]);
5186
+ const getItemPos = (index)=>`${index + 1} of ${data.length}`;
5187
+ const handleMoveItem = (newIndex, oldIndex)=>{
5188
+ const item = data[oldIndex];
5189
+ setLiveText(formatMessage({
5190
+ id: index.getTranslation('dnd.reorder'),
5191
+ defaultMessage: '{item}, moved. New position in list: {position}.'
5192
+ }, {
5193
+ item: item.label ?? item.documentId,
5194
+ position: getItemPos(newIndex)
5195
+ }));
5196
+ /**
5197
+ * Splicing mutates the array, so we need to create a new array
5198
+ */ const newData = [
5199
+ ...data
5200
+ ];
5201
+ const currentRow = data[oldIndex];
5202
+ const startKey = oldIndex > newIndex ? newData[newIndex - 1]?.__temp_key__ : newData[newIndex]?.__temp_key__;
5203
+ const endKey = oldIndex > newIndex ? newData[newIndex]?.__temp_key__ : newData[newIndex + 1]?.__temp_key__;
5204
+ /**
5205
+ * We're moving the relation between two other relations, so
5206
+ * we need to generate a new key that keeps the order
5207
+ */ const [newKey] = fractionalIndexing.generateNKeysBetween(startKey, endKey, 1);
5208
+ newData.splice(oldIndex, 1);
5209
+ newData.splice(newIndex, 0, {
5210
+ ...currentRow,
5211
+ __temp_key__: newKey
5212
+ });
5213
+ /**
5214
+ * Now we diff against the server to understand what's different so we
5215
+ * can keep the connect array nice and tidy. It also needs reversing because
5216
+ * we reverse the relations from the server in the first place.
5217
+ */ const connectedRelations = newData.reduce((acc, relation, currentIndex, array)=>{
5218
+ const relationOnServer = serverData.find((oldRelation)=>oldRelation.id === relation.id);
5219
+ const relationInFront = array[currentIndex + 1];
5220
+ if (!relationOnServer || relationOnServer.__temp_key__ !== relation.__temp_key__) {
5221
+ const position = relationInFront ? {
5222
+ before: relationInFront.documentId,
5223
+ locale: relationInFront.locale,
5224
+ status: 'publishedAt' in relationInFront && relationInFront.publishedAt ? 'published' : 'draft'
5225
+ } : {
5226
+ end: true
5227
+ };
5228
+ const relationWithPosition = {
5229
+ ...relation,
5230
+ ...{
5231
+ apiData: {
5232
+ id: relation.id,
5233
+ documentId: relation.documentId,
5234
+ locale: relation.locale,
5235
+ position
5236
+ }
5237
+ }
5238
+ };
5239
+ return [
5240
+ ...acc,
5241
+ relationWithPosition
5242
+ ];
5243
+ }
5244
+ return acc;
5245
+ }, []).toReversed();
5246
+ field.onChange(`${name}.connect`, connectedRelations);
5247
+ };
5248
+ const handleGrabItem = (index$1)=>{
5249
+ const item = data[index$1];
5250
+ setLiveText(formatMessage({
5251
+ id: index.getTranslation('dnd.grab-item'),
5252
+ defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`
5253
+ }, {
5254
+ item: item.label ?? item.documentId,
5255
+ position: getItemPos(index$1)
5256
+ }));
5257
+ };
5258
+ const handleDropItem = (index$1)=>{
5259
+ const { href: _href, label, ...item } = data[index$1];
5260
+ setLiveText(formatMessage({
5261
+ id: index.getTranslation('dnd.drop-item'),
5262
+ defaultMessage: `{item}, dropped. Final position in list: {position}.`
5263
+ }, {
5264
+ item: label ?? item.documentId,
5265
+ position: getItemPos(index$1)
5266
+ }));
5267
+ };
5268
+ const handleCancel = (index$1)=>{
5269
+ const item = data[index$1];
5270
+ setLiveText(formatMessage({
5271
+ id: index.getTranslation('dnd.cancel-item'),
5272
+ defaultMessage: '{item}, dropped. Re-order cancelled.'
5273
+ }, {
5274
+ item: item.label ?? item.documentId
5275
+ }));
5276
+ };
5277
+ const handleDisconnect = useHandleDisconnect(name, 'RelationsList');
5278
+ /**
5279
+ * These relation types will only ever have one item
5280
+ * in their list, so you can't reorder a single item!
5281
+ */ const canReorder = !ONE_WAY_RELATIONS.includes(relationType);
5282
+ const dynamicListHeight = data.length > RELATIONS_TO_DISPLAY ? Math.min(data.length, RELATIONS_TO_DISPLAY) * (RELATION_ITEM_HEIGHT + RELATION_GUTTER) + RELATION_ITEM_HEIGHT / 2 : Math.min(data.length, RELATIONS_TO_DISPLAY) * (RELATION_ITEM_HEIGHT + RELATION_GUTTER);
5283
+ return /*#__PURE__*/ jsxRuntime.jsxs(ShadowBox, {
5284
+ $overflowDirection: overflow,
5285
+ children: [
5286
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
5287
+ id: ariaDescriptionId,
5288
+ children: formatMessage({
5289
+ id: index.getTranslation('dnd.instructions'),
5290
+ defaultMessage: `Press spacebar to grab and re-order`
5291
+ })
5292
+ }),
5293
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
5294
+ "aria-live": "assertive",
5295
+ children: liveText
5296
+ }),
5297
+ /*#__PURE__*/ jsxRuntime.jsx(reactWindow.FixedSizeList, {
5298
+ height: dynamicListHeight,
5299
+ ref: listRef,
5300
+ outerRef: outerListRef,
5301
+ itemCount: data.length,
5302
+ itemSize: RELATION_ITEM_HEIGHT + RELATION_GUTTER,
5303
+ itemData: {
5304
+ ariaDescribedBy: ariaDescriptionId,
5305
+ canDrag: canReorder,
5306
+ disabled,
5307
+ handleCancel,
5308
+ handleDropItem,
5309
+ handleGrabItem,
5310
+ handleMoveItem,
5311
+ name,
5312
+ handleDisconnect,
5313
+ relations: data,
5314
+ targetModel
5315
+ },
5316
+ itemKey: (index)=>data[index].id,
5317
+ innerElementType: "ol",
5318
+ children: ListItem
5319
+ })
5320
+ ]
5321
+ });
5322
+ };
5323
+ const ShadowBox = styledComponents.styled(designSystem.Box)`
5324
+ position: relative;
5325
+ overflow: hidden;
5326
+ flex: 1;
5327
+
5328
+ &:before,
5329
+ &:after {
5330
+ position: absolute;
5331
+ width: 100%;
5332
+ height: 4px;
5333
+ z-index: 1;
5334
+ }
5335
+
5336
+ &:before {
5337
+ /* TODO: as for DS Table component we would need this to be handled by the DS theme */
5338
+ content: '';
5339
+ background: linear-gradient(rgba(3, 3, 5, 0.2) 0%, rgba(0, 0, 0, 0) 100%);
5340
+ top: 0;
5341
+ opacity: ${({ $overflowDirection })=>$overflowDirection === 'top-bottom' || $overflowDirection === 'top' ? 1 : 0};
5342
+ transition: opacity 0.2s ease-in-out;
5343
+ }
5344
+
5345
+ &:after {
5346
+ /* TODO: as for DS Table component we would need this to be handled by the DS theme */
5347
+ content: '';
5348
+ background: linear-gradient(0deg, rgba(3, 3, 5, 0.2) 0%, rgba(0, 0, 0, 0) 100%);
5349
+ bottom: 0;
5350
+ opacity: ${({ $overflowDirection })=>$overflowDirection === 'top-bottom' || $overflowDirection === 'bottom' ? 1 : 0};
5351
+ transition: opacity 0.2s ease-in-out;
5352
+ }
5353
+ `;
5354
+ const ListItem = ({ data, index: index$1, style })=>{
5355
+ const { ariaDescribedBy, canDrag = false, disabled = false, handleCancel, handleDisconnect, handleDropItem, handleGrabItem, handleMoveItem, name, relations, targetModel } = data;
5356
+ const { formatMessage } = reactIntl.useIntl();
5357
+ const { href, id, label, status, documentId, apiData, locale } = relations[index$1];
5358
+ const [{ handlerId, isDragging, handleKeyDown }, relationRef, dropRef, dragRef, dragPreviewRef] = objects.useDragAndDrop(canDrag && !disabled, {
5359
+ type: `${objects.ItemTypes.RELATION}_${name}`,
5360
+ index: index$1,
5361
+ item: {
5362
+ displayedValue: label,
5363
+ status,
5364
+ id: id,
5365
+ index: index$1
5366
+ },
5367
+ onMoveItem: handleMoveItem,
5368
+ onDropItem: handleDropItem,
5369
+ onGrabItem: handleGrabItem,
5370
+ onCancel: handleCancel,
5371
+ dropSensitivity: objects.DROP_SENSITIVITY.REGULAR
5372
+ });
5373
+ const composedRefs = designSystem.useComposedRefs(relationRef, dragRef);
5374
+ React__namespace.useEffect(()=>{
5375
+ dragPreviewRef(reactDndHtml5Backend.getEmptyImage());
5376
+ }, [
5377
+ dragPreviewRef
5378
+ ]);
5379
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
5380
+ style: style,
5381
+ tag: "li",
5382
+ ref: dropRef,
5383
+ "aria-describedby": ariaDescribedBy,
5384
+ cursor: canDrag ? 'all-scroll' : 'default',
5385
+ children: isDragging ? /*#__PURE__*/ jsxRuntime.jsx(RelationItemPlaceholder, {}) : /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
5386
+ paddingTop: 2,
5387
+ paddingBottom: 2,
5388
+ paddingLeft: canDrag ? 2 : 4,
5389
+ paddingRight: 4,
5390
+ hasRadius: true,
5391
+ borderColor: "neutral200",
5392
+ background: disabled ? 'neutral150' : 'neutral0',
5393
+ justifyContent: "space-between",
5394
+ ref: composedRefs,
5395
+ "data-handler-id": handlerId,
5396
+ children: [
5397
+ /*#__PURE__*/ jsxRuntime.jsxs(FlexWrapper, {
5398
+ gap: 1,
5399
+ children: [
5400
+ canDrag ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
5401
+ tag: "div",
5402
+ role: "button",
5403
+ tabIndex: 0,
5404
+ withTooltip: false,
5405
+ label: formatMessage({
5406
+ id: index.getTranslation('components.RelationInput.icon-button-aria-label'),
5407
+ defaultMessage: 'Drag'
5408
+ }),
5409
+ variant: "ghost",
5410
+ onKeyDown: handleKeyDown,
5411
+ disabled: disabled,
5412
+ children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Drag, {})
5413
+ }) : null,
5414
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
5415
+ width: "100%",
5416
+ minWidth: 0,
5417
+ justifyContent: "space-between",
5418
+ children: [
5419
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
5420
+ minWidth: 0,
5421
+ paddingTop: 1,
5422
+ paddingBottom: 1,
5423
+ paddingRight: 4,
5424
+ children: /*#__PURE__*/ jsxRuntime.jsx(RelationModalForm, {
5425
+ triggerButtonLabel: label,
5426
+ relation: {
5427
+ documentId: documentId ?? apiData?.documentId,
5428
+ model: targetModel,
5429
+ collectionType: getCollectionType(href),
5430
+ params: {
5431
+ locale: locale || apiData?.locale || null
5432
+ }
5433
+ }
5434
+ })
5435
+ }),
5436
+ status ? /*#__PURE__*/ jsxRuntime.jsx(index.DocumentStatus, {
5437
+ status: status
5438
+ }) : null
5439
+ ]
5440
+ })
5441
+ ]
5442
+ }),
5443
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
5444
+ paddingLeft: 4,
5445
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
5446
+ onClick: ()=>handleDisconnect(relations[index$1]),
5447
+ disabled: disabled,
5448
+ label: formatMessage({
5449
+ id: index.getTranslation('relation.disconnect'),
5450
+ defaultMessage: 'Remove'
5451
+ }),
5452
+ variant: "ghost",
5453
+ size: "S",
5454
+ children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Cross, {})
5455
+ })
5456
+ })
5457
+ ]
5458
+ })
5459
+ });
5460
+ };
5461
+ const FlexWrapper = styledComponents.styled(designSystem.Flex)`
5462
+ width: 100%;
5463
+ /* Used to prevent endAction to be pushed out of container */
5464
+ min-width: 0;
5465
+
5466
+ & > div[role='button'] {
5467
+ cursor: all-scroll;
5468
+ }
5469
+ `;
5470
+ const DisconnectButton = styledComponents.styled.button`
5471
+ svg path {
5472
+ fill: ${({ theme, disabled })=>disabled ? theme.colors.neutral600 : theme.colors.neutral500};
5473
+ }
5474
+
5475
+ &:hover svg path,
5476
+ &:focus svg path {
5477
+ fill: ${({ theme, disabled })=>!disabled && theme.colors.neutral600};
5478
+ }
5479
+ `;
5480
+ const LinkEllipsis = styledComponents.styled(designSystem.Link)`
5481
+ display: block;
5482
+
5483
+ & > span {
5484
+ white-space: nowrap;
5485
+ overflow: hidden;
5486
+ text-overflow: ellipsis;
5487
+ display: block;
5488
+ }
5489
+ `;
5490
+ const RelationItemPlaceholder = ()=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
5491
+ paddingTop: 2,
5492
+ paddingBottom: 2,
5493
+ paddingLeft: 4,
5494
+ paddingRight: 4,
5495
+ hasRadius: true,
5496
+ borderStyle: "dashed",
5497
+ borderColor: "primary600",
5498
+ borderWidth: "1px",
5499
+ background: "primary100",
5500
+ height: `calc(100% - ${RELATION_GUTTER}px)`
5501
+ });
5502
+ const MemoizedRelationsField = /*#__PURE__*/ React__namespace.memo(RelationsField);
5503
+
4251
5504
  const uidApi = index.contentManagerApi.injectEndpoints({
4252
5505
  endpoints: (builder)=>({
4253
5506
  getDefaultUID: builder.query({
@@ -4304,7 +5557,7 @@ const UIDInput = /*#__PURE__*/ React__namespace.forwardRef(({ hint, label, label
4304
5557
  const [showRegenerate, setShowRegenerate] = React__namespace.useState(false);
4305
5558
  const isCloning = reactRouterDom.useMatch(index.CLONE_PATH) !== null;
4306
5559
  const field = strapiAdmin.useField(name);
4307
- const debouncedValue = relations.useDebounce(field.value, 300);
5560
+ const debouncedValue = usePrev.useDebounce(field.value, 300);
4308
5561
  const hasChanged = debouncedValue !== field.initialValue;
4309
5562
  const { toggleNotification } = strapiAdmin.useNotification();
4310
5563
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
@@ -6423,17 +7676,19 @@ const MemoizedWysiwyg = /*#__PURE__*/ React__namespace.memo(Wysiwyg);
6423
7676
  * specifically to be used in the EditView of the content-manager this understands
6424
7677
  * the complete EditFieldLayout and will handle RBAC conditions and rendering CM specific
6425
7678
  * components such as Blocks / Relations.
6426
- */ const InputRenderer = ({ visible, hint: providedHint, ...props })=>{
6427
- const { id, document, collectionType } = index.useDoc();
6428
- const isFormDisabled = strapiAdmin.useForm('InputRenderer', (state)=>state.disabled);
7679
+ */ const InputRenderer = ({ visible, hint: providedHint, document, ...props })=>{
7680
+ const { model: rootModel } = index.useDoc();
7681
+ const documentLayout = index.useDocumentLayout(document.schema?.uid ?? rootModel);
7682
+ const collectionType = document.schema?.kind === 'collectionType' ? 'collection-types' : 'single-types';
6429
7683
  const isInDynamicZone = useDynamicZone('isInDynamicZone', (state)=>state.isInDynamicZone);
7684
+ const isFormDisabled = strapiAdmin.useForm('InputRenderer', (state)=>state.disabled);
6430
7685
  const canCreateFields = index.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canCreateFields);
6431
7686
  const canReadFields = index.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canReadFields);
6432
7687
  const canUpdateFields = index.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUpdateFields);
6433
7688
  const canUserAction = index.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUserAction);
6434
- let idToCheck = id;
7689
+ let idToCheck = document.document?.documentId;
6435
7690
  if (collectionType === index.SINGLE_TYPES) {
6436
- idToCheck = document?.documentId;
7691
+ idToCheck = document?.document?.documentId;
6437
7692
  }
6438
7693
  const editableFields = idToCheck ? canUpdateFields : canCreateFields;
6439
7694
  const readableFields = idToCheck ? canReadFields : canCreateFields;
@@ -6447,7 +7702,7 @@ const MemoizedWysiwyg = /*#__PURE__*/ React__namespace.memo(Wysiwyg);
6447
7702
  props.attribute.customField
6448
7703
  ] : undefined);
6449
7704
  const hint = useFieldHint(providedHint, props.attribute);
6450
- const { edit: { components } } = index.useDocLayout();
7705
+ const components = documentLayout.edit.components;
6451
7706
  // We pass field in case of Custom Fields to keep backward compatibility
6452
7707
  const field = strapiAdmin.useField(props.name);
6453
7708
  if (!visible) {
@@ -6524,14 +7779,7 @@ const MemoizedWysiwyg = /*#__PURE__*/ React__namespace.memo(Wysiwyg);
6524
7779
  disabled: fieldIsDisabled
6525
7780
  });
6526
7781
  case 'relation':
6527
- if (window.strapi.future.isEnabled('unstableRelationsOnTheFly')) {
6528
- return /*#__PURE__*/ jsxRuntime.jsx(Relations.MemoizedUnstableRelationsField, {
6529
- ...props,
6530
- hint: hint,
6531
- disabled: fieldIsDisabled
6532
- });
6533
- }
6534
- return /*#__PURE__*/ jsxRuntime.jsx(Relations.MemoizedRelationsField, {
7782
+ return /*#__PURE__*/ jsxRuntime.jsx(MemoizedRelationsField, {
6535
7783
  ...props,
6536
7784
  hint: hint,
6537
7785
  disabled: fieldIsDisabled
@@ -6628,7 +7876,7 @@ const getMinMax = (attribute)=>{
6628
7876
  };
6629
7877
  }
6630
7878
  };
6631
- const MemoizedInputRenderer = /*#__PURE__*/ React.memo(InputRenderer);
7879
+ const MemoizedInputRenderer = /*#__PURE__*/ React__namespace.memo(InputRenderer);
6632
7880
 
6633
7881
  const RESPONSIVE_CONTAINER_BREAKPOINTS = {
6634
7882
  sm: '27.5rem'
@@ -6636,16 +7884,22 @@ const RESPONSIVE_CONTAINER_BREAKPOINTS = {
6636
7884
  const ResponsiveGridRoot = styledComponents.styled(designSystem.Grid.Root)`
6637
7885
  container-type: inline-size;
6638
7886
  `;
6639
- const ResponsiveGridItem = styledComponents.styled(designSystem.Grid.Item)`
6640
- grid-column: span 12;
6641
-
6642
- @container (min-width: ${RESPONSIVE_CONTAINER_BREAKPOINTS.sm}) {
6643
- ${({ col })=>col && `grid-column: span ${col};`}
6644
- }
6645
- `;
6646
- const FormLayout = ({ layout, hasBackground = false })=>{
7887
+ const ResponsiveGridItem = /**
7888
+ * TODO:
7889
+ * JSDOM cannot handle container queries.
7890
+ * This is a temporary workaround so that tests do not fail in the CI when jestdom throws an error
7891
+ * for failing to parse the stylesheet.
7892
+ */ process.env.NODE_ENV !== 'test' ? styledComponents.styled(designSystem.Grid.Item)`
7893
+ grid-column: span 12;
7894
+ @container (min-width: ${RESPONSIVE_CONTAINER_BREAKPOINTS.sm}) {
7895
+ ${({ col })=>col && `grid-column: span ${col};`}
7896
+ }
7897
+ ` : styledComponents.styled(designSystem.Grid.Item)`
7898
+ grid-column: span 12;
7899
+ `;
7900
+ const FormLayout = ({ layout, document, hasBackground = true })=>{
6647
7901
  const { formatMessage } = reactIntl.useIntl();
6648
- const { model } = index.useDoc();
7902
+ const model = document.schema?.modelName;
6649
7903
  return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
6650
7904
  direction: "column",
6651
7905
  alignItems: "stretch",
@@ -6670,13 +7924,14 @@ const FormLayout = ({ layout, hasBackground = false })=>{
6670
7924
  direction: "column",
6671
7925
  alignItems: "stretch",
6672
7926
  children: /*#__PURE__*/ jsxRuntime.jsx(MemoizedInputRenderer, {
6673
- ...fieldWithTranslatedLabel
7927
+ ...fieldWithTranslatedLabel,
7928
+ document: document
6674
7929
  })
6675
7930
  })
6676
7931
  }, field.name);
6677
7932
  }
6678
7933
  return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
6679
- ...!hasBackground && {
7934
+ ...hasBackground && {
6680
7935
  padding: 6,
6681
7936
  borderColor: 'neutral150',
6682
7937
  background: 'neutral0',
@@ -6704,7 +7959,8 @@ const FormLayout = ({ layout, hasBackground = false })=>{
6704
7959
  direction: "column",
6705
7960
  alignItems: "stretch",
6706
7961
  children: /*#__PURE__*/ jsxRuntime.jsx(MemoizedInputRenderer, {
6707
- ...fieldWithTranslatedLabel
7962
+ ...fieldWithTranslatedLabel,
7963
+ document: document
6708
7964
  })
6709
7965
  }, field.name);
6710
7966
  })
@@ -6718,9 +7974,10 @@ const FormLayout = ({ layout, hasBackground = false })=>{
6718
7974
  const NonRepeatableComponent = ({ attribute, name, children, layout })=>{
6719
7975
  const { formatMessage } = reactIntl.useIntl();
6720
7976
  const { value } = strapiAdmin.useField(name);
6721
- const level = Relations.useComponent('NonRepeatableComponent', (state)=>state.level);
7977
+ const level = useComponent('NonRepeatableComponent', (state)=>state.level);
6722
7978
  const isNested = level > 0;
6723
- return /*#__PURE__*/ jsxRuntime.jsx(Relations.ComponentProvider, {
7979
+ const currentDocument = index.useDocumentContext('NonRepeatableComponent', (state)=>state.document);
7980
+ return /*#__PURE__*/ jsxRuntime.jsx(ComponentProvider, {
6724
7981
  id: value?.id,
6725
7982
  uid: attribute.component,
6726
7983
  level: level + 1,
@@ -6760,7 +8017,8 @@ const NonRepeatableComponent = ({ attribute, name, children, layout })=>{
6760
8017
  children: children({
6761
8018
  ...field,
6762
8019
  label: translatedLabel,
6763
- name: completeFieldName
8020
+ name: completeFieldName,
8021
+ document: currentDocument
6764
8022
  })
6765
8023
  }, completeFieldName);
6766
8024
  })
@@ -6778,7 +8036,8 @@ const RepeatableComponent = ({ attribute, disabled, name, mainField, children, l
6778
8036
  const search = React__namespace.useMemo(()=>new URLSearchParams(searchString), [
6779
8037
  searchString
6780
8038
  ]);
6781
- const { components } = index.useDoc();
8039
+ const currentDocument = index.useDocumentContext('RepeatableComponent', (state)=>state.document);
8040
+ const components = currentDocument.components;
6782
8041
  const { value = [], error, rawError } = strapiAdmin.useField(name);
6783
8042
  const addFieldRow = strapiAdmin.useForm('RepeatableComponent', (state)=>state.addFieldRow);
6784
8043
  const moveFieldRow = strapiAdmin.useForm('RepeatableComponent', (state)=>state.moveFieldRow);
@@ -6906,7 +8165,7 @@ const RepeatableComponent = ({ attribute, disabled, name, mainField, children, l
6906
8165
  }));
6907
8166
  };
6908
8167
  const ariaDescriptionId = React__namespace.useId();
6909
- const level = Relations.useComponent('RepeatableComponent', (state)=>state.level);
8168
+ const level = useComponent('RepeatableComponent', (state)=>state.level);
6910
8169
  if (value.length === 0) {
6911
8170
  return /*#__PURE__*/ jsxRuntime.jsx(Initializer, {
6912
8171
  disabled: disabled,
@@ -6936,7 +8195,7 @@ const RepeatableComponent = ({ attribute, disabled, name, mainField, children, l
6936
8195
  children: [
6937
8196
  value.map(({ __temp_key__: key, id }, index)=>{
6938
8197
  const nameWithIndex = `${name}.${index}`;
6939
- return /*#__PURE__*/ jsxRuntime.jsx(Relations.ComponentProvider, {
8198
+ return /*#__PURE__*/ jsxRuntime.jsx(ComponentProvider, {
6940
8199
  // id is always a number in a component
6941
8200
  id: id,
6942
8201
  uid: attribute.component,
@@ -6981,7 +8240,8 @@ const RepeatableComponent = ({ attribute, disabled, name, mainField, children, l
6981
8240
  children: children({
6982
8241
  ...field,
6983
8242
  label: translatedLabel,
6984
- name: completeFieldName
8243
+ name: completeFieldName,
8244
+ document: currentDocument
6985
8245
  })
6986
8246
  }, completeFieldName);
6987
8247
  })
@@ -7052,8 +8312,8 @@ const Component = ({ disabled, index: index$1, name, mainField = {
7052
8312
  * components are not affected by the drag and drop of the parent component in
7053
8313
  * their own re-ordering context.
7054
8314
  */ const componentKey = name.split('.').slice(0, -1).join('.');
7055
- const [{ handlerId, isDragging, handleKeyDown }, boxRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop.useDragAndDrop(!disabled, {
7056
- type: `${useDragAndDrop.ItemTypes.COMPONENT}_${componentKey}`,
8315
+ const [{ handlerId, isDragging, handleKeyDown }, boxRef, dropRef, dragRef, dragPreviewRef] = objects.useDragAndDrop(!disabled, {
8316
+ type: `${objects.ItemTypes.COMPONENT}_${componentKey}`,
7057
8317
  index: index$1,
7058
8318
  item: {
7059
8319
  index: index$1,
@@ -7143,7 +8403,7 @@ const ComponentInput = ({ label, required, name, attribute, disabled, labelActio
7143
8403
  const { formatMessage } = reactIntl.useIntl();
7144
8404
  const field = strapiAdmin.useField(name);
7145
8405
  const showResetComponent = !attribute.repeatable && field.value && !disabled;
7146
- const { components } = index.useDoc();
8406
+ const components = index.useDocumentContext('ComponentInput', (state)=>state.document.components);
7147
8407
  const handleInitialisationClick = ()=>{
7148
8408
  const schema = components[attribute.component];
7149
8409
  const form = index.createDefaultForm(schema, components);
@@ -7208,8 +8468,11 @@ const ComponentInput = ({ label, required, name, attribute, disabled, labelActio
7208
8468
  };
7209
8469
  const MemoizedComponentInput = /*#__PURE__*/ React__namespace.memo(ComponentInput);
7210
8470
 
8471
+ exports.DisconnectButton = DisconnectButton;
7211
8472
  exports.DynamicZone = DynamicZone;
8473
+ exports.FlexWrapper = FlexWrapper;
7212
8474
  exports.FormLayout = FormLayout;
8475
+ exports.LinkEllipsis = LinkEllipsis;
7213
8476
  exports.MemoizedBlocksInput = MemoizedBlocksInput;
7214
8477
  exports.MemoizedComponentInput = MemoizedComponentInput;
7215
8478
  exports.MemoizedUIDInput = MemoizedUIDInput;
@@ -7218,4 +8481,4 @@ exports.NotAllowedInput = NotAllowedInput;
7218
8481
  exports.useDynamicZone = useDynamicZone;
7219
8482
  exports.useFieldHint = useFieldHint;
7220
8483
  exports.useLazyComponents = useLazyComponents;
7221
- //# sourceMappingURL=Input-Bv-rqfYH.js.map
8484
+ //# sourceMappingURL=Input-C2r54bIL.js.map