@contentful/field-editor-reference 5.9.0 → 5.11.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 (199) hide show
  1. package/dist/cjs/__fixtures__/FakeSdk.js +183 -0
  2. package/dist/cjs/__fixtures__/asset/index.js +37 -0
  3. package/dist/cjs/__fixtures__/content-type/index.js +16 -0
  4. package/dist/cjs/__fixtures__/entry/index.js +33 -0
  5. package/dist/cjs/__fixtures__/fixtures.js +71 -0
  6. package/dist/cjs/__fixtures__/locale/index.js +40 -0
  7. package/dist/cjs/__fixtures__/space/index.js +16 -0
  8. package/dist/cjs/assets/MultipleMediaEditor.js +86 -0
  9. package/dist/cjs/assets/SingleMediaEditor.js +69 -0
  10. package/dist/cjs/assets/WrappedAssetCard/AssetCardActions.js +125 -0
  11. package/dist/cjs/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +171 -0
  12. package/dist/cjs/assets/WrappedAssetCard/WrappedAssetCard.js +159 -0
  13. package/dist/cjs/assets/WrappedAssetCard/WrappedAssetLink.js +130 -0
  14. package/dist/cjs/assets/index.js +24 -0
  15. package/dist/cjs/common/EntityStore.js +420 -0
  16. package/dist/cjs/common/MultipleReferenceEditor.js +164 -0
  17. package/dist/cjs/common/ReferenceEditor.js +74 -0
  18. package/dist/cjs/common/SingleReferenceEditor.js +118 -0
  19. package/dist/cjs/common/SortableLinkList.js +95 -0
  20. package/dist/cjs/common/customCardTypes.js +44 -0
  21. package/dist/cjs/common/useAccessApi.js +19 -0
  22. package/dist/cjs/common/useContentTypePermissions.js +54 -0
  23. package/dist/cjs/common/useEditorPermissions.js +77 -0
  24. package/dist/cjs/common/useEditorPermissions.spec.js +205 -0
  25. package/dist/cjs/components/AssetThumbnail/AssetThumbnail.js +62 -0
  26. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryLinkButton.js +102 -0
  27. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryLinkButton.spec.js +254 -0
  28. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryMenuTrigger.js +199 -0
  29. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.js +190 -0
  30. package/dist/cjs/components/CreateEntryLinkButton/useGlobalMouseUp.js +19 -0
  31. package/dist/cjs/components/LinkActions/CombinedLinkActions.js +167 -0
  32. package/dist/cjs/components/LinkActions/LinkActions.js +123 -0
  33. package/dist/cjs/components/LinkActions/LinkEntityActions.js +186 -0
  34. package/dist/cjs/components/LinkActions/NoLinkPermissionsInfo.js +54 -0
  35. package/dist/cjs/components/LinkActions/helpers.js +78 -0
  36. package/dist/cjs/components/LinkActions/redesignStyles.js +44 -0
  37. package/dist/cjs/components/LinkActions/styles.js +33 -0
  38. package/dist/cjs/components/MissingEntityCard/MissingEntityCard.js +75 -0
  39. package/dist/cjs/components/MissingEntityCard/styles.js +29 -0
  40. package/dist/cjs/components/ScheduledIconWithTooltip/ScheduleTooltip.js +75 -0
  41. package/dist/cjs/components/ScheduledIconWithTooltip/ScheduledIconWithTooltip.js +81 -0
  42. package/dist/cjs/components/ScheduledIconWithTooltip/formatDateAndTime.js +45 -0
  43. package/dist/cjs/components/SpaceName/SpaceName.js +91 -0
  44. package/dist/cjs/components/index.js +44 -0
  45. package/dist/cjs/entries/MultipleEntryReferenceEditor.js +86 -0
  46. package/dist/cjs/entries/SingleEntryReferenceEditor.js +74 -0
  47. package/dist/cjs/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +189 -0
  48. package/dist/cjs/entries/WrappedEntryCard/WrappedEntryCard.js +181 -0
  49. package/dist/cjs/entries/index.js +24 -0
  50. package/dist/cjs/index.js +92 -0
  51. package/dist/cjs/resources/Cards/ContentfulEntryCard.js +87 -0
  52. package/dist/cjs/resources/Cards/ResourceCard.js +111 -0
  53. package/dist/cjs/resources/Cards/UnsupportedEntityCard.js +64 -0
  54. package/dist/cjs/resources/MultipleResourceReferenceEditor.js +157 -0
  55. package/dist/cjs/resources/MultipleResourceReferenceEditor.spec.js +297 -0
  56. package/dist/cjs/resources/SingleResourceReferenceEditor.js +87 -0
  57. package/dist/cjs/resources/SingleResourceReferenceEditor.spec.js +161 -0
  58. package/dist/cjs/resources/index.js +19 -0
  59. package/dist/cjs/resources/testHelpers/resourceEditorHelpers.js +121 -0
  60. package/dist/cjs/resources/useResourceLinkActions.js +88 -0
  61. package/dist/cjs/types.js +22 -0
  62. package/dist/cjs/utils/fromFieldValidations.js +54 -0
  63. package/dist/esm/__fixtures__/FakeSdk.js +173 -0
  64. package/dist/esm/__fixtures__/asset/index.js +6 -0
  65. package/dist/esm/__fixtures__/content-type/index.js +2 -0
  66. package/dist/esm/__fixtures__/entry/index.js +5 -0
  67. package/dist/esm/__fixtures__/fixtures.js +6 -0
  68. package/dist/esm/__fixtures__/locale/index.js +15 -0
  69. package/dist/esm/__fixtures__/space/index.js +2 -0
  70. package/dist/esm/assets/MultipleMediaEditor.js +37 -0
  71. package/dist/esm/assets/SingleMediaEditor.js +20 -0
  72. package/dist/esm/assets/WrappedAssetCard/AssetCardActions.js +63 -0
  73. package/dist/esm/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +122 -0
  74. package/dist/esm/assets/WrappedAssetCard/WrappedAssetCard.js +105 -0
  75. package/dist/esm/assets/WrappedAssetCard/WrappedAssetLink.js +76 -0
  76. package/dist/esm/assets/index.js +3 -0
  77. package/dist/esm/common/EntityStore.js +347 -0
  78. package/dist/esm/common/MultipleReferenceEditor.js +111 -0
  79. package/dist/esm/common/ReferenceEditor.js +20 -0
  80. package/dist/esm/common/SingleReferenceEditor.js +70 -0
  81. package/dist/esm/common/SortableLinkList.js +41 -0
  82. package/dist/esm/common/customCardTypes.js +1 -0
  83. package/dist/esm/common/useAccessApi.js +9 -0
  84. package/dist/esm/common/useContentTypePermissions.js +44 -0
  85. package/dist/esm/common/useEditorPermissions.js +67 -0
  86. package/dist/esm/common/useEditorPermissions.spec.js +201 -0
  87. package/dist/esm/components/AssetThumbnail/AssetThumbnail.js +13 -0
  88. package/dist/esm/components/CreateEntryLinkButton/CreateEntryLinkButton.js +48 -0
  89. package/dist/esm/components/CreateEntryLinkButton/CreateEntryLinkButton.spec.js +206 -0
  90. package/dist/esm/components/CreateEntryLinkButton/CreateEntryMenuTrigger.js +145 -0
  91. package/dist/esm/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.js +142 -0
  92. package/dist/esm/components/CreateEntryLinkButton/useGlobalMouseUp.js +9 -0
  93. package/dist/esm/components/LinkActions/CombinedLinkActions.js +118 -0
  94. package/dist/esm/components/LinkActions/LinkActions.js +66 -0
  95. package/dist/esm/components/LinkActions/LinkEntityActions.js +127 -0
  96. package/dist/esm/components/LinkActions/NoLinkPermissionsInfo.js +5 -0
  97. package/dist/esm/components/LinkActions/helpers.js +57 -0
  98. package/dist/esm/components/LinkActions/redesignStyles.js +18 -0
  99. package/dist/esm/components/LinkActions/styles.js +10 -0
  100. package/dist/esm/components/MissingEntityCard/MissingEntityCard.js +26 -0
  101. package/dist/esm/components/MissingEntityCard/styles.js +11 -0
  102. package/dist/esm/components/ScheduledIconWithTooltip/ScheduleTooltip.js +18 -0
  103. package/dist/esm/components/ScheduledIconWithTooltip/ScheduledIconWithTooltip.js +32 -0
  104. package/dist/esm/components/ScheduledIconWithTooltip/formatDateAndTime.js +19 -0
  105. package/dist/esm/components/SpaceName/SpaceName.js +37 -0
  106. package/dist/esm/components/index.js +8 -0
  107. package/dist/esm/entries/MultipleEntryReferenceEditor.js +37 -0
  108. package/dist/esm/entries/SingleEntryReferenceEditor.js +25 -0
  109. package/dist/esm/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +135 -0
  110. package/dist/esm/entries/WrappedEntryCard/WrappedEntryCard.js +127 -0
  111. package/dist/esm/entries/index.js +3 -0
  112. package/dist/esm/index.js +7 -0
  113. package/dist/esm/resources/Cards/ContentfulEntryCard.js +38 -0
  114. package/dist/esm/resources/Cards/ResourceCard.js +62 -0
  115. package/dist/esm/resources/Cards/UnsupportedEntityCard.js +15 -0
  116. package/dist/esm/resources/MultipleResourceReferenceEditor.js +104 -0
  117. package/dist/esm/resources/MultipleResourceReferenceEditor.spec.js +254 -0
  118. package/dist/esm/resources/SingleResourceReferenceEditor.js +33 -0
  119. package/dist/esm/resources/SingleResourceReferenceEditor.spec.js +118 -0
  120. package/dist/esm/resources/index.js +2 -0
  121. package/dist/esm/resources/testHelpers/resourceEditorHelpers.js +103 -0
  122. package/dist/esm/resources/useResourceLinkActions.js +78 -0
  123. package/dist/esm/types.js +1 -0
  124. package/dist/esm/utils/fromFieldValidations.js +39 -0
  125. package/dist/{__fixtures__ → types/__fixtures__}/FakeSdk.d.ts +8 -8
  126. package/dist/{__fixtures__ → types/__fixtures__}/asset/index.d.ts +6 -6
  127. package/dist/{__fixtures__ → types/__fixtures__}/content-type/index.d.ts +2 -2
  128. package/dist/{__fixtures__ → types/__fixtures__}/entry/index.d.ts +5 -5
  129. package/dist/{__fixtures__ → types/__fixtures__}/fixtures.d.ts +6 -6
  130. package/dist/{__fixtures__ → types/__fixtures__}/locale/index.d.ts +42 -42
  131. package/dist/{__fixtures__ → types/__fixtures__}/space/index.d.ts +2 -2
  132. package/dist/{assets → types/assets}/MultipleMediaEditor.d.ts +10 -10
  133. package/dist/types/assets/SingleMediaEditor.d.ts +10 -0
  134. package/dist/{assets → types/assets}/WrappedAssetCard/AssetCardActions.d.ts +11 -11
  135. package/dist/{assets → types/assets}/WrappedAssetCard/FetchingWrappedAssetCard.d.ts +17 -17
  136. package/dist/{assets → types/assets}/WrappedAssetCard/WrappedAssetCard.d.ts +24 -24
  137. package/dist/{assets → types/assets}/WrappedAssetCard/WrappedAssetLink.d.ts +16 -16
  138. package/dist/{assets → types/assets}/index.d.ts +3 -3
  139. package/dist/{common → types/common}/EntityStore.d.ts +62 -62
  140. package/dist/{common → types/common}/MultipleReferenceEditor.d.ts +25 -25
  141. package/dist/{common → types/common}/ReferenceEditor.d.ts +46 -46
  142. package/dist/{common → types/common}/SingleReferenceEditor.d.ts +24 -24
  143. package/dist/{common → types/common}/SortableLinkList.d.ts +19 -19
  144. package/dist/{common → types/common}/customCardTypes.d.ts +29 -29
  145. package/dist/types/common/useAccessApi.d.ts +16 -0
  146. package/dist/{common → types/common}/useContentTypePermissions.d.ts +17 -17
  147. package/dist/{common → types/common}/useEditorPermissions.d.ts +17 -17
  148. package/dist/types/common/useEditorPermissions.spec.d.ts +1 -0
  149. package/dist/{components → types/components}/AssetThumbnail/AssetThumbnail.d.ts +7 -7
  150. package/dist/{components → types/components}/CreateEntryLinkButton/CreateEntryLinkButton.d.ts +19 -19
  151. package/dist/types/components/CreateEntryLinkButton/CreateEntryLinkButton.spec.d.ts +1 -0
  152. package/dist/{components → types/components}/CreateEntryLinkButton/CreateEntryMenuTrigger.d.ts +31 -31
  153. package/dist/types/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.d.ts +1 -0
  154. package/dist/{components → types/components}/CreateEntryLinkButton/useGlobalMouseUp.d.ts +1 -1
  155. package/dist/{components → types/components}/LinkActions/CombinedLinkActions.d.ts +10 -10
  156. package/dist/{components → types/components}/LinkActions/LinkActions.d.ts +26 -26
  157. package/dist/{components → types/components}/LinkActions/LinkEntityActions.d.ts +24 -24
  158. package/dist/types/components/LinkActions/NoLinkPermissionsInfo.d.ts +2 -0
  159. package/dist/{components → types/components}/LinkActions/helpers.d.ts +26 -26
  160. package/dist/{components → types/components}/LinkActions/redesignStyles.d.ts +3 -3
  161. package/dist/{components → types/components}/LinkActions/styles.d.ts +2 -2
  162. package/dist/{components → types/components}/MissingEntityCard/MissingEntityCard.d.ts +8 -8
  163. package/dist/{components → types/components}/MissingEntityCard/styles.d.ts +2 -2
  164. package/dist/{components → types/components}/ScheduledIconWithTooltip/ScheduleTooltip.d.ts +11 -11
  165. package/dist/{components → types/components}/ScheduledIconWithTooltip/ScheduledIconWithTooltip.d.ts +10 -10
  166. package/dist/{components → types/components}/ScheduledIconWithTooltip/formatDateAndTime.d.ts +15 -15
  167. package/dist/types/components/SpaceName/SpaceName.d.ts +6 -0
  168. package/dist/{components → types/components}/index.d.ts +9 -9
  169. package/dist/{entries → types/entries}/MultipleEntryReferenceEditor.d.ts +3 -3
  170. package/dist/{entries → types/entries}/SingleEntryReferenceEditor.d.ts +8 -8
  171. package/dist/{entries → types/entries}/WrappedEntryCard/FetchingWrappedEntryCard.d.ts +18 -18
  172. package/dist/{entries → types/entries}/WrappedEntryCard/WrappedEntryCard.d.ts +35 -35
  173. package/dist/{entries → types/entries}/index.d.ts +3 -3
  174. package/dist/{index.d.ts → types/index.d.ts} +10 -8
  175. package/dist/{resources → types/resources}/Cards/ContentfulEntryCard.d.ts +21 -21
  176. package/dist/{resources → types/resources}/Cards/ResourceCard.d.ts +12 -12
  177. package/dist/{resources → types/resources}/Cards/UnsupportedEntityCard.d.ts +4 -4
  178. package/dist/{resources → types/resources}/MultipleResourceReferenceEditor.d.ts +7 -7
  179. package/dist/types/resources/MultipleResourceReferenceEditor.spec.d.ts +1 -0
  180. package/dist/{resources → types/resources}/SingleResourceReferenceEditor.d.ts +7 -7
  181. package/dist/types/resources/SingleResourceReferenceEditor.spec.d.ts +1 -0
  182. package/dist/{resources → types/resources}/index.d.ts +2 -2
  183. package/dist/{resources → types/resources}/testHelpers/resourceEditorHelpers.d.ts +50 -50
  184. package/dist/{resources → types/resources}/useResourceLinkActions.d.ts +7 -7
  185. package/dist/{types.d.ts → types/types.d.ts} +104 -104
  186. package/dist/{utils → types/utils}/fromFieldValidations.d.ts +21 -21
  187. package/package.json +25 -11
  188. package/CHANGELOG.md +0 -860
  189. package/dist/assets/SingleMediaEditor.d.ts +0 -10
  190. package/dist/common/useAccessApi.d.ts +0 -16
  191. package/dist/components/LinkActions/NoLinkPermissionsInfo.d.ts +0 -2
  192. package/dist/components/SpaceName/SpaceName.d.ts +0 -6
  193. package/dist/field-editor-reference.cjs.development.js +0 -2753
  194. package/dist/field-editor-reference.cjs.development.js.map +0 -1
  195. package/dist/field-editor-reference.cjs.production.min.js +0 -2
  196. package/dist/field-editor-reference.cjs.production.min.js.map +0 -1
  197. package/dist/field-editor-reference.esm.js +0 -2727
  198. package/dist/field-editor-reference.esm.js.map +0 -1
  199. package/dist/index.js +0 -8
@@ -1,2727 +0,0 @@
1
- import React__default, { useState, useRef, useEffect, createElement, Fragment, useMemo, useCallback } from 'react';
2
- import { Menu, TextInput, Button, Paragraph, Card, Flex, SectionHeading, IconButton, Tooltip, Text, EntryCard, MenuItem, MenuDivider, AssetCard } from '@contentful/f36-components';
3
- import { SearchIcon, PlusIcon, ChevronDownIcon, LinkIcon, CloseIcon, FolderOpenTrimmedIcon, ClockIcon } from '@contentful/f36-icons';
4
- import tokens from '@contentful/f36-tokens';
5
- import { css, cx } from 'emotion';
6
- import get from 'lodash-es/get';
7
- import moment from 'moment';
8
- import { FieldConnector, isValidImage, entityHelpers, shortenStorageUnit } from '@contentful/field-editor-shared';
9
- import deepEqual from 'deep-equal';
10
- import { useQueryClient, useQuery, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
11
- import constate from 'constate';
12
- import { createClient } from 'contentful-management';
13
- import PQueue from 'p-queue';
14
- import isNumber from 'lodash-es/isNumber';
15
- import arrayMove from 'array-move';
16
- import { SortableContainer, SortableHandle, SortableElement } from 'react-sortable-hoc';
17
- import mimetype from '@contentful/mimetype';
18
- import { useInView } from 'react-intersection-observer';
19
-
20
- /* eslint-disable @typescript-eslint/no-explicit-any */
21
- const MAX_ITEMS_WITHOUT_SEARCH = 5;
22
- const menuPlacementMap = {
23
- 'bottom-left': 'bottom-start',
24
- 'bottom-right': 'bottom-end'
25
- };
26
- const styles = {
27
- wrapper: /*#__PURE__*/css({
28
- position: 'relative'
29
- }),
30
- inputWrapper: /*#__PURE__*/css({
31
- position: 'relative',
32
- padding: `0 ${tokens.spacing2Xs}`
33
- }),
34
- searchInput: /*#__PURE__*/css({
35
- paddingRight: tokens.spacingXl,
36
- textOverflow: 'ellipsis'
37
- }),
38
- searchIcon: /*#__PURE__*/css({
39
- position: 'absolute',
40
- right: tokens.spacingM,
41
- top: tokens.spacingS,
42
- zIndex: /*#__PURE__*/Number(tokens.zIndexDefault),
43
- fill: tokens.gray600
44
- }),
45
- separator: /*#__PURE__*/css({
46
- background: tokens.gray200,
47
- margin: '10px 0'
48
- }),
49
- dropdownList: /*#__PURE__*/css({
50
- borderColor: tokens.gray200
51
- })
52
- };
53
- const CreateEntryMenuTrigger = ({
54
- contentTypes,
55
- suggestedContentTypeId,
56
- contentTypesLabel,
57
- onSelect,
58
- testId,
59
- dropdownSettings = {
60
- position: 'bottom-left'
61
- },
62
- customDropdownItems,
63
- children
64
- }) => {
65
- const [isOpen, setOpen] = useState(false);
66
- const [isSelecting, setSelecting] = useState(false);
67
- const [searchInput, setSearchInput] = useState('');
68
- const wrapper = useRef(null);
69
- const textField = useRef(null);
70
- const menuListRef = useRef(null);
71
- /*
72
- By default, dropdown wraps it's content, so it's width = the width of the widest item
73
- During search, menu items change, and so the widest menu item can change
74
- This leads to menu always changing it's width
75
- To prevent this, we get the width of the menu item after the first mount of a dropdown (when all the content is displayed)
76
- And hardcode it through the class name. This way we ensure that even during search the menu will keep that max width
77
- That it had on initial mount and that fits any menu item in has
78
- */
79
-
80
- const [dropdownWidth, setDropdownWidth] = useState();
81
- const hasDropdown = contentTypes.length > 1 || !!customDropdownItems;
82
-
83
- const closeMenu = () => setOpen(false);
84
-
85
- useEffect(() => {
86
- if (isOpen) {
87
- setTimeout(() => {
88
- var _textField$current, _textField$current$qu;
89
-
90
- (_textField$current = textField.current) == null ? void 0 : (_textField$current$qu = _textField$current.querySelector('input')) == null ? void 0 : _textField$current$qu.focus({
91
- preventScroll: true
92
- });
93
- }, 200);
94
- }
95
- }, [isOpen]);
96
- useEffect(() => {
97
- if (isOpen && !dropdownWidth) {
98
- var _menuListRef$current;
99
-
100
- setDropdownWidth((_menuListRef$current = menuListRef.current) == null ? void 0 : _menuListRef$current.clientWidth);
101
- }
102
- }, [isOpen, dropdownWidth]);
103
-
104
- const handleSelect = item => {
105
- closeMenu();
106
- const res = onSelect(item.sys.id); // TODO: Convert to controllable component.
107
-
108
- if (res && typeof res.then === 'function') {
109
- setSelecting(true);
110
- res.then(() => setSelecting(false), () => setSelecting(false));
111
- }
112
- };
113
-
114
- const handleMenuOpen = () => {
115
- if (hasDropdown) {
116
- setOpen(true);
117
- } else {
118
- handleSelect(contentTypes[0]);
119
- }
120
- };
121
-
122
- useEffect(() => {
123
- if (!isOpen) {
124
- setSearchInput('');
125
- }
126
- }, [isOpen]);
127
-
128
- const renderSearchResultsCount = resultsLength => resultsLength ? React__default.createElement(Menu.SectionTitle, {
129
- testId: "add-entru-menu-search-results"
130
- }, resultsLength, " result", resultsLength > 1 ? 's' : '') : null;
131
-
132
- const isSearchable = contentTypes.length > MAX_ITEMS_WITHOUT_SEARCH;
133
- const maxDropdownHeight = suggestedContentTypeId ? 300 : 250;
134
- const suggestedContentType = contentTypes.find(ct => ct.sys.id === suggestedContentTypeId);
135
- const filteredContentTypes = contentTypes.filter(ct => !searchInput || get(ct, 'name', 'Untitled').toLowerCase().includes(searchInput.toLowerCase()));
136
- return React__default.createElement("span", {
137
- className: styles.wrapper,
138
- ref: wrapper,
139
- "data-test-id": testId
140
- }, React__default.createElement(Menu, {
141
- placement: menuPlacementMap[dropdownSettings.position],
142
- isAutoalignmentEnabled: dropdownSettings.isAutoalignmentEnabled,
143
- isOpen: isOpen,
144
- onClose: closeMenu,
145
- onOpen: handleMenuOpen
146
- }, React__default.createElement(Menu.Trigger, null, children({
147
- isOpen,
148
- isSelecting
149
- })), isOpen && React__default.createElement(Menu.List, {
150
- className: styles.dropdownList,
151
- style: {
152
- width: dropdownWidth != undefined ? `${dropdownWidth}px` : undefined,
153
- maxHeight: `${maxDropdownHeight}px`
154
- },
155
- ref: menuListRef,
156
- testId: "add-entry-menu"
157
- }, Boolean(customDropdownItems) && React__default.createElement(React__default.Fragment, null, customDropdownItems, React__default.createElement(Menu.Divider, null)), isSearchable && React__default.createElement(React__default.Fragment, null, React__default.createElement("div", {
158
- ref: textField,
159
- className: styles.inputWrapper
160
- }, React__default.createElement(TextInput, {
161
- className: styles.searchInput,
162
- placeholder: "Search all content types",
163
- testId: "add-entry-menu-search",
164
- value: searchInput,
165
- onChange: e => setSearchInput(e.target.value)
166
- }), React__default.createElement(SearchIcon, {
167
- className: styles.searchIcon
168
- })), React__default.createElement(Menu.Divider, null)), searchInput && renderSearchResultsCount(filteredContentTypes.length), suggestedContentType && !searchInput && React__default.createElement(React__default.Fragment, null, React__default.createElement(Menu.SectionTitle, null, "Suggested Content Type"), React__default.createElement(Menu.Item, {
169
- testId: "suggested",
170
- onClick: () => handleSelect(suggestedContentType)
171
- }, get(suggestedContentType, 'name')), React__default.createElement(Menu.Divider, null)), !searchInput && React__default.createElement(Menu.SectionTitle, null, contentTypesLabel), filteredContentTypes.length ? filteredContentTypes.map((contentType, i) => React__default.createElement(Menu.Item, {
172
- testId: "contentType",
173
- key: `${get(contentType, 'name')}-${i}`,
174
- onClick: () => handleSelect(contentType)
175
- }, get(contentType, 'name', 'Untitled'))) : React__default.createElement(Menu.Item, {
176
- testId: "add-entru-menu-search-results"
177
- }, "No results found"))));
178
- };
179
- CreateEntryMenuTrigger.defaultProps = {
180
- testId: 'create-entry-button-menu-trigger',
181
- contentTypesLabel: 'All Content Types'
182
- };
183
-
184
- const standardStyles = {
185
- spinnerMargin: /*#__PURE__*/css({
186
- marginRight: tokens.spacingXs
187
- }),
188
- action: undefined
189
- };
190
- const redesignStyles = { ...standardStyles,
191
- action: /*#__PURE__*/css({
192
- textDecoration: 'none',
193
- fontWeight: 'bold',
194
- maxWidth: '300px'
195
- })
196
- };
197
- const CreateEntryLinkButton = ({
198
- contentTypes,
199
- onSelect,
200
- customDropdownItems,
201
- text,
202
- testId,
203
- hasPlusIcon = false,
204
- useExperimentalStyles,
205
- suggestedContentTypeId,
206
- dropdownSettings,
207
- disabled = false
208
- }) => {
209
- contentTypes = contentTypes.sort((a, b) => a.name.localeCompare(b.name));
210
- const suggestedContentType = contentTypes.find(ct => ct.sys.id === suggestedContentTypeId);
211
- const buttonText = text || `Add ${get(suggestedContentType || (contentTypes.length === 1 ? contentTypes[0] : {}), 'name', 'entry')}`;
212
- const hasDropdown = contentTypes.length > 1 || customDropdownItems; // TODO: Introduce `icon: string` and remove `hasPlusIcon` or remove "Plus" if we keep new layout.
213
-
214
- const plusIcon = hasPlusIcon ? React__default.createElement(PlusIcon, null) : undefined; // TODO: Always use "New content" here if we fully switch to new layout.
215
-
216
- const contentTypesLabel = useExperimentalStyles ? 'New content' : undefined;
217
- const styles = useExperimentalStyles ? redesignStyles : standardStyles;
218
- return React__default.createElement(CreateEntryMenuTrigger, {
219
- contentTypes: contentTypes,
220
- suggestedContentTypeId: suggestedContentTypeId,
221
- contentTypesLabel: contentTypesLabel,
222
- onSelect: onSelect,
223
- testId: testId,
224
- dropdownSettings: dropdownSettings,
225
- customDropdownItems: customDropdownItems
226
- }, ({
227
- isSelecting
228
- }) => React__default.createElement(Button, {
229
- endIcon: hasDropdown ? React__default.createElement(ChevronDownIcon, null) : undefined,
230
- variant: "secondary",
231
- className: styles.action,
232
- isDisabled: disabled || isSelecting,
233
- startIcon: isSelecting ? undefined : plusIcon,
234
- size: "small",
235
- testId: "create-entry-link-button",
236
- isLoading: isSelecting
237
- }, buttonText));
238
- };
239
-
240
- function NoLinkPermissionsInfo() {
241
- return React__default.createElement(Paragraph, null, "You don't have permission to view this content or this field is not correctly configured. Contact your administrator for help.");
242
- }
243
-
244
- const container = /*#__PURE__*/css({
245
- display: 'flex',
246
- width: '100%',
247
- marginTop: tokens.spacingS
248
- });
249
- const separator = /*#__PURE__*/css({
250
- marginRight: tokens.spacingXl
251
- });
252
-
253
- const defaultEntryLabels = {
254
- createNew: props => props != null && props.contentType ? `Create new ${props.contentType} and link` : 'Create new entry and link',
255
- linkExisting: props => props != null && props.canLinkMultiple ? 'Link existing entries' : 'Link existing entry'
256
- };
257
- const defaultAssetLabels = {
258
- createNew: () => `Create new asset and link`,
259
- linkExisting: props => props != null && props.canLinkMultiple ? 'Link existing assets' : 'Link existing asset'
260
- };
261
- const testIds = {
262
- dropdown: 'linkEditor.dropdown',
263
- createAndLink: 'linkEditor.createAndLink',
264
- createAndLinkWrapper: 'create-entry-button-menu-trigger',
265
- linkExisting: 'linkEditor.linkExisting'
266
- };
267
- function LinkActions(props) {
268
- if (props.isFull) {
269
- return null; // Don't render link actions if we reached max allowed links.
270
- }
271
-
272
- const defaultLabels = props.entityType === 'Entry' ? defaultEntryLabels : defaultAssetLabels;
273
- const labels = { ...defaultLabels,
274
- ...props.actionLabels
275
- };
276
- return createElement("div", {
277
- className: container
278
- }, props.canCreateEntity && createElement(Fragment, null, props.entityType === 'Entry' && createElement(CreateEntryLinkButton, {
279
- testId: testIds.createAndLink,
280
- disabled: props.isDisabled,
281
- text: labels.createNew({
282
- contentType: props.contentTypes.length === 1 ? props.contentTypes[0].name : undefined
283
- }),
284
- contentTypes: props.contentTypes,
285
- hasPlusIcon: true,
286
- onSelect: contentTypeId => {
287
- return contentTypeId ? props.onCreate(contentTypeId, props.itemsLength) : Promise.resolve();
288
- }
289
- }), props.entityType === 'Asset' && createElement(Button, {
290
- isDisabled: props.isDisabled,
291
- testId: testIds.createAndLink,
292
- onClick: () => {
293
- props.onCreate(undefined, props.itemsLength);
294
- },
295
- variant: "secondary",
296
- startIcon: createElement(PlusIcon, null),
297
- size: "small"
298
- }, labels.createNew()), createElement("span", {
299
- className: separator
300
- })), props.canLinkEntity && createElement(Button, {
301
- isDisabled: props.isDisabled,
302
- testId: testIds.linkExisting,
303
- onClick: () => {
304
- props.onLinkExisting();
305
- },
306
- variant: "secondary",
307
- startIcon: createElement(LinkIcon, null),
308
- size: "small"
309
- }, labels.linkExisting({
310
- canLinkMultiple: props.canLinkMultiple
311
- })), !props.canCreateEntity && !props.canLinkEntity && createElement(NoLinkPermissionsInfo, null));
312
- }
313
-
314
- const container$1 = /*#__PURE__*/css({
315
- display: 'flex',
316
- border: `1px dashed ${tokens.gray500}`,
317
- borderRadius: tokens.borderRadiusMedium,
318
- justifyContent: 'center',
319
- padding: tokens.spacingXl
320
- });
321
- const action = /*#__PURE__*/css({
322
- textDecoration: 'none',
323
- fontWeight: 'bold'
324
- });
325
-
326
- const testIds$1 = { ...testIds,
327
- actionsWrapper: 'link-actions-menu-trigger'
328
- };
329
- /**
330
- * Alternative, experimental alternative to <LinkActions /> that is planned to
331
- * replace the current default LinkActions in reference and media editors.
332
- *
333
- * Places both actions to create and link new, as well as link existing, behind
334
- * one action dropdown and introduces new copy for action labels.
335
- */
336
-
337
- function CombinedLinkActions(props) {
338
- if (props.isFull) {
339
- return null; // Don't render link actions if we reached max allowed links.
340
- } // We don't want to render a spacious container in case there are are already
341
- // assets linked (in case of entries, always show it) as the border wouldn't be
342
- // nicely aligned with asset cards.
343
-
344
-
345
- const hideEmptyCard = props.entityType === 'Asset' && !props.isEmpty;
346
- return createElement("div", {
347
- className: hideEmptyCard ? '' : container$1
348
- }, !props.canCreateEntity && !props.canLinkEntity && createElement(NoLinkPermissionsInfo, null), props.entityType === 'Entry' && createElement(CombinedEntryLinkActions, { ...props
349
- }), props.entityType === 'Asset' && createElement(CombinedAssetLinkActions, { ...props
350
- }));
351
- }
352
-
353
- function CombinedEntryLinkActions(props) {
354
- if (props.canCreateEntity) {
355
- return createElement(CreateEntryLinkButton, {
356
- testId: testIds$1.actionsWrapper,
357
- disabled: props.isDisabled,
358
- text: props.combinedActionsLabel || 'Add content',
359
- contentTypes: props.contentTypes,
360
- hasPlusIcon: true,
361
- useExperimentalStyles: true,
362
- dropdownSettings: {
363
- position: 'bottom-left'
364
- },
365
- onSelect: contentTypeId => {
366
- return contentTypeId ? props.onCreate(contentTypeId) : Promise.resolve();
367
- },
368
- customDropdownItems: props.canLinkEntity ? createElement(Menu.Item, {
369
- testId: testIds$1.linkExisting,
370
- onClick: () => {
371
- props.onLinkExisting();
372
- }
373
- }, "Add existing content") : undefined
374
- });
375
- } else if (props.canLinkEntity) {
376
- return createElement(Button, {
377
- isDisabled: props.isDisabled,
378
- testId: testIds$1.linkExisting,
379
- className: action,
380
- onClick: () => {
381
- props.onLinkExisting();
382
- },
383
- variant: "secondary",
384
- startIcon: createElement(LinkIcon, null),
385
- size: "small"
386
- }, "Add existing content");
387
- }
388
-
389
- return null;
390
- }
391
-
392
- function CombinedAssetLinkActions(props) {
393
- const [isOpen, setOpen] = useState(false);
394
-
395
- if (!props.canLinkEntity || !props.canCreateEntity) {
396
- if (props.canLinkEntity) {
397
- return createElement(Button, {
398
- isDisabled: props.isDisabled,
399
- testId: testIds$1.linkExisting,
400
- className: action,
401
- onClick: () => {
402
- props.onLinkExisting();
403
- },
404
- variant: "secondary",
405
- startIcon: createElement(PlusIcon, null),
406
- size: "small"
407
- }, "Add existing media");
408
- }
409
-
410
- if (props.canCreateEntity) {
411
- return createElement(Button, {
412
- isDisabled: props.isDisabled,
413
- testId: testIds$1.createAndLink,
414
- className: action,
415
- onClick: () => {
416
- props.onCreate();
417
- },
418
- variant: "secondary",
419
- startIcon: createElement(PlusIcon, null),
420
- size: "small"
421
- }, "Add media");
422
- }
423
-
424
- return null;
425
- } // TODO: If we fully switch to this new layout, make a more generic `CreateEntityLinkButton`
426
- // that works without content types to cover asset use-case.
427
-
428
-
429
- return createElement(Menu, {
430
- isOpen: isOpen,
431
- onClose: () => {
432
- setOpen(false);
433
- },
434
- onOpen: () => {
435
- setOpen(true);
436
- }
437
- }, createElement(Menu.Trigger, null, createElement(Button, {
438
- endIcon: createElement(ChevronDownIcon, null),
439
- isDisabled: props.isDisabled,
440
- testId: testIds$1.actionsWrapper,
441
- className: action,
442
- variant: "secondary",
443
- startIcon: createElement(PlusIcon, null),
444
- size: "small"
445
- }, "Add media")), isOpen && createElement(Menu.List, {
446
- testId: testIds$1.dropdown
447
- }, createElement(Menu.Item, {
448
- testId: testIds$1.linkExisting,
449
- onClick: () => {
450
- props.onLinkExisting();
451
- }
452
- }, "Add existing media"), createElement(Menu.Item, {
453
- testId: testIds$1.createAndLink,
454
- onClick: () => {
455
- props.onCreate();
456
- }
457
- }, "Add new media")));
458
- }
459
-
460
- const card = /*#__PURE__*/css({
461
- position: 'relative'
462
- });
463
- const squareCard = /*#__PURE__*/css({
464
- display: 'flex',
465
- alignItems: 'center',
466
- width: '135px',
467
- height: '160px',
468
- textAlign: 'center'
469
- });
470
-
471
- function MissingEntityCard(props) {
472
- return React__default.createElement(Card, {
473
- className: card,
474
- testId: "cf-ui-missing-entry-card"
475
- }, React__default.createElement(Flex, {
476
- alignItems: "center",
477
- justifyContent: "space-between"
478
- }, React__default.createElement("div", {
479
- className: props.asSquare ? squareCard : ''
480
- }, React__default.createElement(SectionHeading, {
481
- marginBottom: "none"
482
- }, props.entityType, " is missing or inaccessible")), !props.isDisabled && props.onRemove && React__default.createElement(IconButton, {
483
- variant: "transparent",
484
- icon: React__default.createElement(CloseIcon, {
485
- variant: "muted"
486
- }),
487
- "aria-label": "Delete",
488
- onClick: () => {
489
- props.onRemove && props.onRemove();
490
- }
491
- })));
492
- }
493
-
494
- const getContentTypeIds = contentTypes => contentTypes.map(ct => ct.sys.id);
495
-
496
- async function createEntity(props) {
497
- if (props.entityType === 'Entry') {
498
- if (!props.contentTypeId) {
499
- return {};
500
- }
501
-
502
- const {
503
- entity,
504
- slide
505
- } = await props.sdk.navigator.openNewEntry(props.contentTypeId, {
506
- slideIn: true
507
- });
508
- return {
509
- entity,
510
- slide
511
- };
512
- } else {
513
- const {
514
- entity,
515
- slide
516
- } = await props.sdk.navigator.openNewAsset({
517
- slideIn: true
518
- });
519
- return {
520
- entity,
521
- slide
522
- };
523
- }
524
- }
525
- async function selectSingleEntity(props) {
526
- if (props.entityType === 'Entry') {
527
- return await props.sdk.dialogs.selectSingleEntry({
528
- locale: props.sdk.field.locale,
529
- contentTypes: getContentTypeIds(props.editorPermissions.readableContentTypes)
530
- });
531
- } else {
532
- return props.sdk.dialogs.selectSingleAsset({
533
- locale: props.sdk.field.locale,
534
- mimetypeGroups: props.editorPermissions.validations.mimetypeGroups
535
- });
536
- }
537
- }
538
- async function selectMultipleEntities(props) {
539
- var _props$editorPermissi, _props$editorPermissi2;
540
-
541
- const value = props.sdk.field.getValue();
542
- const linkCount = Array.isArray(value) ? value.length : value ? 1 : 0; // TODO: Why not always set `min: 1` by default? Does it make sense to enforce
543
- // user to select as many entities as the field's "min" requires? What if e.g.
544
- // "min" is 4 and the user wants to insert 2 entities first, then create 2 new ones?
545
-
546
- const min = Math.max((((_props$editorPermissi = props.editorPermissions.validations.numberOfLinks) == null ? void 0 : _props$editorPermissi.min) || 1) - linkCount, 1); // TODO: Consider same for max. If e.g. "max" is 4, we disable the button if the
547
- // user wants to select 5 but we show no information why the button is disabled.
548
-
549
- const max = (((_props$editorPermissi2 = props.editorPermissions.validations.numberOfLinks) == null ? void 0 : _props$editorPermissi2.max) || +Infinity) - linkCount;
550
-
551
- if (props.entityType === 'Entry') {
552
- return await props.sdk.dialogs.selectMultipleEntries({
553
- locale: props.sdk.field.locale,
554
- contentTypes: getContentTypeIds(props.editorPermissions.readableContentTypes),
555
- min,
556
- max
557
- });
558
- } else {
559
- return props.sdk.dialogs.selectMultipleAssets({
560
- locale: props.sdk.field.locale,
561
- mimetypeGroups: props.editorPermissions.validations.mimetypeGroups,
562
- min,
563
- max
564
- });
565
- }
566
- }
567
-
568
- function useLinkActionsProps(props) {
569
- var _editorPermissions$va;
570
-
571
- const {
572
- sdk,
573
- editorPermissions,
574
- entityType,
575
- canLinkMultiple,
576
- isDisabled,
577
- actionLabels,
578
- itemsLength
579
- } = props;
580
- const maxLinksCount = (_editorPermissions$va = editorPermissions.validations.numberOfLinks) == null ? void 0 : _editorPermissions$va.max;
581
- const value = sdk.field.getValue();
582
- const linkCount = Array.isArray(value) ? value.length : value ? 1 : 0;
583
- const isFull = !!maxLinksCount && maxLinksCount <= linkCount;
584
- const isEmpty = linkCount === 0;
585
- const onCreated = useCallback((entity, index = itemsLength, slide) => {
586
- props.onCreate(entity.sys.id, index);
587
- props.onAction && props.onAction({
588
- type: 'create_and_link',
589
- entity: entityType,
590
- entityData: entity,
591
- slide,
592
- index
593
- });
594
- }, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
595
- [entityType, props.onCreate, props.onAction]);
596
- const onLinkedExisting = useCallback((entities, index = itemsLength) => {
597
- props.onLink(entities.map(item => item.sys.id), index);
598
- entities.forEach((entity, i) => {
599
- props.onAction && props.onAction({
600
- type: 'select_and_link',
601
- entity: entityType,
602
- entityData: entity,
603
- index: index === undefined ? undefined : index + i
604
- });
605
- });
606
- }, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
607
- [entityType, props.onLink, props.onAction]);
608
- const onCreate = useCallback(async (contentTypeId, index) => {
609
- const {
610
- entity,
611
- slide
612
- } = await createEntity({
613
- sdk,
614
- entityType,
615
- contentTypeId
616
- });
617
-
618
- if (!entity) {
619
- return;
620
- }
621
-
622
- onCreated(entity, index, slide);
623
- }, [sdk, entityType, onCreated]);
624
- const onLinkExisting = useCallback(async index => {
625
- const entity = await selectSingleEntity({
626
- sdk,
627
- entityType,
628
- editorPermissions
629
- });
630
-
631
- if (!entity) {
632
- return;
633
- }
634
-
635
- onLinkedExisting([entity], index);
636
- }, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
637
- [sdk, entityType, onLinkedExisting]);
638
- const onLinkSeveralExisting = useCallback(async index => {
639
- const entities = await selectMultipleEntities({
640
- sdk,
641
- entityType,
642
- editorPermissions
643
- });
644
-
645
- if (!entities || entities.length === 0) {
646
- return;
647
- }
648
-
649
- onLinkedExisting(entities, index);
650
- }, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
651
- [sdk, entityType, onLinkedExisting]); // FIXME: The memoization might rerun every time due to the always changing callback identities above
652
-
653
- return useMemo(() => ({
654
- entityType,
655
- canLinkMultiple,
656
- isDisabled,
657
- isEmpty,
658
- isFull,
659
- canCreateEntity: editorPermissions.canCreateEntity,
660
- canLinkEntity: editorPermissions.canLinkEntity,
661
- contentTypes: editorPermissions.creatableContentTypes,
662
- onCreate,
663
- onLinkExisting: canLinkMultiple ? onLinkSeveralExisting : onLinkExisting,
664
- actionLabels,
665
- onCreated,
666
- onLinkedExisting,
667
- itemsLength
668
- }), // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
669
- [entityType, canLinkMultiple, isDisabled, isEmpty, isFull, editorPermissions.canCreateEntity, editorPermissions.canLinkEntity, actionLabels, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
670
- editorPermissions.creatableContentTypes.map(ct => ct.sys.id).join(':'), onCreate, onLinkExisting, onLinkSeveralExisting, onCreated, onLinkedExisting, itemsLength]);
671
- }
672
- function LinkEntityActions({
673
- renderCustomActions,
674
- ...props
675
- }) {
676
- return renderCustomActions ? renderCustomActions(props) : createElement(LinkActions, { ...props
677
- });
678
- }
679
- function CombinedLinkEntityActions({
680
- renderCustomActions,
681
- ...props
682
- }) {
683
- return renderCustomActions ? renderCustomActions(props) : createElement(CombinedLinkActions, { ...props
684
- });
685
- }
686
-
687
- // eslint-disable-next-line -- TODO: describe this disable no-restricted-imports
688
- /**
689
- * @param {Date|string} date A valid constructor argument for moment()
690
- * @param {boolean=} short Render only Today/Tomorrow/Yesterday if valid. Defaults to false
691
- */
692
-
693
- const formatDate = (date, short) => {
694
- switch (moment().startOf('day').diff(moment(date).startOf('day'), 'days')) {
695
- case 0:
696
- return short ? 'Today' : `Today, ${moment(date).format('DD MMM YYYY')}`;
697
-
698
- case -1:
699
- return short ? 'Tomorrow' : `Tomorrow, ${moment(date).format('DD MMM YYYY')}`;
700
-
701
- case 1:
702
- return short ? 'Yesterday' : `Yesterday, ${moment(date).format('DD MMM YYYY')}`;
703
-
704
- default:
705
- return moment(date).format('ddd, DD MMM YYYY');
706
- }
707
- };
708
- /**
709
- * Returns the time portion of a date in the local time in the format H:MM AM/PM
710
- *
711
- * == Examples
712
- * * `T15:36:45.000Z` => 3:36 PM (if in +0:00 offset)
713
- */
714
-
715
- const formatTime = date => {
716
- return moment.utc(date).local().format('h:mm A');
717
- };
718
- const formatDateAndTime = (date, short) => {
719
- return `${formatDate(date, short)} at ${formatTime(date)}`;
720
- };
721
-
722
- const getScheduleTooltipContent = ({
723
- job,
724
- jobsCount
725
- }) => {
726
- return `Will ${job.action.toLowerCase()} ${formatDateAndTime(job.scheduledFor.datetime).toLowerCase()}
727
- ${jobsCount > 1 ? `+ ${jobsCount - 1} more` : ''}`;
728
- };
729
- const ScheduleTooltip = ({
730
- job,
731
- jobsCount,
732
- children
733
- }) => {
734
- return React__default.createElement(Tooltip, {
735
- placement: "top",
736
- testId: job.sys.id,
737
- as: "div",
738
- content: getScheduleTooltipContent({
739
- job,
740
- jobsCount
741
- })
742
- }, children);
743
- };
744
-
745
- const ScheduledIconWithTooltip = ({
746
- entityType,
747
- entityId,
748
- getEntityScheduledActions,
749
- children
750
- }) => {
751
- const [status, setStatus] = React__default.useState({
752
- type: 'loading'
753
- });
754
- React__default.useEffect(() => {
755
- getEntityScheduledActions(entityType, entityId).then(data => {
756
- setStatus({
757
- type: 'loaded',
758
- jobs: data
759
- });
760
- }).catch(e => {
761
- setStatus({
762
- type: 'error',
763
- error: e
764
- });
765
- }); // This should only be ever called once. Following the eslint hint to add used
766
- // dependencies will cause page freeze (infinite loop)
767
- // eslint-disable-next-line -- TODO: describe this disable
768
- }, []);
769
-
770
- if (status.type === 'loading' || status.type === 'error') {
771
- return null;
772
- }
773
-
774
- const jobs = status.jobs ?? [];
775
-
776
- if (jobs.length === 0) {
777
- return null;
778
- }
779
-
780
- const mostRelevantJob = jobs[0];
781
- return React__default.createElement(ScheduleTooltip, {
782
- job: mostRelevantJob,
783
- jobsCount: jobs.length
784
- }, children);
785
- };
786
-
787
- const dimensions = {
788
- width: 70,
789
- height: 70
790
- };
791
- function AssetThumbnail(props) {
792
- return React__default.createElement("img", {
793
- alt: props.file.fileName,
794
- src: `${props.file.url}?w=${dimensions.width}&h=${dimensions.height}&fit=thumb`,
795
- height: dimensions.height,
796
- width: dimensions.width
797
- });
798
- }
799
-
800
- const globalQueue = /*#__PURE__*/new PQueue({
801
- concurrency: 50
802
- });
803
- class UnsupportedError extends Error {
804
- constructor(message) {
805
- super(message);
806
- this.isUnsupportedError = void 0;
807
- this.isUnsupportedError = true;
808
- }
809
-
810
- }
811
- function isUnsupportedError(value) {
812
- return typeof value === 'object' && (value == null ? void 0 : value.isUnsupportedError) === true;
813
- }
814
-
815
- const isEntityQueryKey = queryKey => {
816
- return Array.isArray(queryKey) && (queryKey[0] === 'Entry' || queryKey[0] === 'Asset') && queryKey.length === 4;
817
- };
818
-
819
- async function fetchContentfulEntry(params) {
820
- const {
821
- urn,
822
- fetch,
823
- options
824
- } = params;
825
- const resourceId = urn.split(':', 6)[5];
826
- const [, spaceId,, entryId] = resourceId.split('/');
827
- const environmentId = 'master';
828
- const [space, entry] = await Promise.all([fetch(['space', spaceId], ({
829
- cmaClient
830
- }) => cmaClient.space.get({
831
- spaceId
832
- }), options), fetch(['entry', spaceId, environmentId, entryId], ({
833
- cmaClient
834
- }) => cmaClient.entry.get({
835
- spaceId,
836
- environmentId,
837
- entryId
838
- }), options)]);
839
- const contentTypeId = entry.sys.contentType.sys.id;
840
- const [contentType, defaultLocaleCode] = await Promise.all([fetch(['contentType', spaceId, environmentId, contentTypeId], ({
841
- cmaClient
842
- }) => cmaClient.contentType.get({
843
- contentTypeId,
844
- spaceId,
845
- environmentId
846
- }), options), fetch(['defaultLocale', spaceId, environmentId], async ({
847
- cmaClient
848
- }) => {
849
- var _locales$items$find;
850
-
851
- const locales = await cmaClient.locale.getMany({
852
- spaceId,
853
- environmentId,
854
- query: {
855
- limit: 100
856
- }
857
- });
858
- const defaultLocaleCode = (_locales$items$find = locales.items.find(locale => locale.default)) == null ? void 0 : _locales$items$find.code;
859
- return defaultLocaleCode;
860
- }, options)]);
861
- return {
862
- defaultLocaleCode,
863
- resource: entry,
864
- space: space,
865
- contentType: contentType
866
- };
867
- }
868
-
869
- const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = /*#__PURE__*/constate(function useInitServices(props) {
870
- const currentSpaceId = props.sdk.ids.space;
871
- const currentEnvironmentId = props.sdk.ids.environmentAlias ?? props.sdk.ids.environment;
872
- const environmentIds = useMemo(() => [props.sdk.ids.environmentAlias, props.sdk.ids.environment], [props.sdk.ids.environmentAlias, props.sdk.ids.environment]);
873
- const queryClient = useQueryClient();
874
- const queryCache = queryClient.getQueryCache();
875
- const entityChangeUnsubscribers = useRef({});
876
- const cmaClient = useMemo(() => createClient({
877
- apiAdapter: props.sdk.cmaAdapter
878
- }, {
879
- type: 'plain'
880
- }), [props.sdk.cmaAdapter]);
881
- const queryQueue = useMemo(() => {
882
- if (props.queryConcurrency) {
883
- return new PQueue({
884
- concurrency: props.queryConcurrency
885
- });
886
- }
887
-
888
- return globalQueue;
889
- }, [props.queryConcurrency]);
890
- const fetch = useCallback(function fetch(queryKey, fn, options = {}) {
891
- const {
892
- priority,
893
- ...queryOptions
894
- } = options;
895
- return queryClient.fetchQuery(queryKey, () => queryQueue.add(() => fn({
896
- cmaClient
897
- }), {
898
- priority
899
- }), queryOptions);
900
- }, [queryClient, queryQueue, cmaClient]);
901
- const getEntity = useCallback(function getEntity(entityType, entityId, options) {
902
- const spaceId = (options == null ? void 0 : options.spaceId) ?? currentSpaceId;
903
- const environmentId = (options == null ? void 0 : options.environmentId) ?? currentEnvironmentId;
904
- const queryKey = [entityType, entityId, spaceId, environmentId];
905
- return fetch(queryKey, ({
906
- cmaClient
907
- }) => {
908
- if (entityType === 'Entry') {
909
- return cmaClient.entry.get({
910
- entryId: entityId,
911
- spaceId,
912
- environmentId
913
- });
914
- }
915
-
916
- if (entityType === 'Asset') {
917
- return cmaClient.asset.get({
918
- assetId: entityId,
919
- spaceId,
920
- environmentId
921
- });
922
- }
923
-
924
- throw new UnsupportedError('Unsupported entity type');
925
- }, options);
926
- }, [fetch, currentSpaceId, currentEnvironmentId]);
927
- /**
928
- * Fetch all scheduled actions for a given entity.
929
- * This function fetches all schedules for all entries and then returns
930
- * a filtered result based on the entityID provided.
931
- *
932
- * The result is then reused/cached for subsequent calls to this function.
933
- */
934
-
935
- const getEntityScheduledActions = useCallback(function getEntityScheduledActions(entityType, entityId, options) {
936
- // This is fixed to force the cache to reuse previous results
937
- const fixedEntityCacheId = 'scheduledActionEntityId'; // A space+environment combo can only have up to 500 scheduled actions
938
- // With this request we fetch all schedules and can reuse the results.
939
- // See https://www.contentful.com/developers/docs/references/content-management-api/#/reference/scheduled-actions/limitations
940
-
941
- const maxScheduledActions = 500;
942
- const spaceId = (options == null ? void 0 : options.spaceId) ?? currentSpaceId;
943
- const environmentId = (options == null ? void 0 : options.environmentId) ?? currentEnvironmentId;
944
- const queryKey = ['scheduled-actions', entityType, fixedEntityCacheId, spaceId, environmentId]; // Fetch + Filter by entity ID in the end
945
-
946
- return fetch(queryKey, async ({
947
- cmaClient
948
- }) => {
949
- const response = await cmaClient.scheduledActions.getMany({
950
- spaceId,
951
- query: {
952
- 'environment.sys.id': environmentId,
953
- 'sys.status[in]': 'scheduled',
954
- order: 'scheduledFor.datetime',
955
- limit: maxScheduledActions
956
- }
957
- });
958
- return response.items;
959
- }, options).then(items => items.filter(action => action.entity.sys.id === entityId));
960
- }, [fetch, currentSpaceId, currentEnvironmentId]);
961
- const getResource = useCallback(function getResource(resourceType, urn, options) {
962
- const queryKey = ['Resource', resourceType, urn];
963
- return fetch(queryKey, () => {
964
- if (resourceType === 'Contentful:Entry') {
965
- return fetchContentfulEntry({
966
- fetch,
967
- urn,
968
- options
969
- });
970
- }
971
-
972
- throw new UnsupportedError('Unsupported resource type');
973
- }, options);
974
- }, [fetch]);
975
- const isSameSpaceEntityQueryKey = useCallback(queryKey => {
976
- const isEntityKey = isEntityQueryKey(queryKey);
977
- const isSameSpaceEntityKey = isEntityKey && queryKey[2] === currentSpaceId && environmentIds.includes(queryKey[3]);
978
- return isSameSpaceEntityKey;
979
- }, [currentSpaceId, environmentIds]); // @ts-expect-error ...
980
-
981
- const onEntityChanged = props.sdk.space.onEntityChanged;
982
- const onSlideInNavigation = props.sdk.navigator.onSlideInNavigation;
983
- useEffect(() => {
984
- function findSameSpaceQueries() {
985
- return queryCache.findAll({
986
- type: 'active',
987
- predicate: query => isSameSpaceEntityQueryKey(query.queryKey)
988
- });
989
- }
990
-
991
- if (typeof onEntityChanged !== 'function') {
992
- return onSlideInNavigation(({
993
- oldSlideLevel,
994
- newSlideLevel
995
- }) => {
996
- if (oldSlideLevel > newSlideLevel) {
997
- findSameSpaceQueries().forEach(query => {
998
- // automatically refetches the query
999
- void queryClient.invalidateQueries(query.queryKey);
1000
- });
1001
- }
1002
- });
1003
- }
1004
-
1005
- const subscribeQuery = ({
1006
- queryKey,
1007
- queryHash
1008
- }) => {
1009
- const [entityType, entityId] = queryKey;
1010
- entityChangeUnsubscribers.current[queryHash] = onEntityChanged(entityType, entityId, data => {
1011
- queryClient.setQueryData(queryKey, data);
1012
- });
1013
- };
1014
-
1015
- findSameSpaceQueries().forEach(subscribeQuery);
1016
- const unsubscribe = queryCache.subscribe(event => {
1017
- if (!event) {
1018
- return;
1019
- }
1020
-
1021
- const {
1022
- type,
1023
- query
1024
- } = event;
1025
- const {
1026
- queryKey,
1027
- queryHash
1028
- } = query;
1029
-
1030
- if (!isSameSpaceEntityQueryKey(queryKey)) {
1031
- return;
1032
- }
1033
-
1034
- if (type === 'added') {
1035
- subscribeQuery(query);
1036
- }
1037
-
1038
- if (type === 'removed') {
1039
- var _entityChangeUnsubscr, _entityChangeUnsubscr2;
1040
-
1041
- // calling unsubscribe
1042
- (_entityChangeUnsubscr = (_entityChangeUnsubscr2 = entityChangeUnsubscribers.current)[queryHash]) == null ? void 0 : _entityChangeUnsubscr.call(_entityChangeUnsubscr2);
1043
- }
1044
- });
1045
- return () => {
1046
- unsubscribe();
1047
- Object.values(entityChangeUnsubscribers.current).forEach(off => off());
1048
- entityChangeUnsubscribers.current = {};
1049
- };
1050
- }, [onEntityChanged, queryCache, isSameSpaceEntityQueryKey, queryClient, getEntity, onSlideInNavigation]);
1051
- return {
1052
- ids: props.sdk.ids,
1053
- cmaClient,
1054
- fetch,
1055
- getResource,
1056
- getEntity,
1057
- getEntityScheduledActions
1058
- };
1059
- }, ({
1060
- fetch
1061
- }) => fetch, ({
1062
- getResource,
1063
- getEntity,
1064
- getEntityScheduledActions
1065
- }) => ({
1066
- getResource,
1067
- getEntity,
1068
- getEntityScheduledActions
1069
- }), ({
1070
- ids
1071
- }) => ({
1072
- environment: ids.environmentAlias ?? ids.environment,
1073
- space: ids.space
1074
- }));
1075
- function useEntity(entityType, entityId, options) {
1076
- const {
1077
- space,
1078
- environment
1079
- } = useCurrentIds();
1080
- const {
1081
- getEntity
1082
- } = useEntityLoader();
1083
- const queryKey = [entityType, entityId, (options == null ? void 0 : options.spaceId) ?? space, (options == null ? void 0 : options.environmentId) ?? environment];
1084
- const {
1085
- status,
1086
- data
1087
- } = useQuery(queryKey, () => getEntity(entityType, entityId, options), {
1088
- enabled: options == null ? void 0 : options.enabled
1089
- });
1090
- return {
1091
- status,
1092
- data
1093
- };
1094
- }
1095
- function useResource(resourceType, urn, options) {
1096
- const queryKey = ['Resource', resourceType, urn];
1097
- const {
1098
- getResource
1099
- } = useEntityLoader();
1100
- const {
1101
- status,
1102
- data,
1103
- error
1104
- } = useQuery(queryKey, () => getResource(resourceType, urn, options), {
1105
- enabled: options == null ? void 0 : options.enabled
1106
- });
1107
- return {
1108
- status,
1109
- data,
1110
- error
1111
- };
1112
- }
1113
-
1114
- function EntityProvider({
1115
- children,
1116
- ...props
1117
- }) {
1118
- const reactQueryClient = useMemo(() => {
1119
- const queryCache = new QueryCache();
1120
- const queryClient = new QueryClient({
1121
- queryCache,
1122
- defaultOptions: {
1123
- queries: {
1124
- useErrorBoundary: false,
1125
- refetchOnWindowFocus: false,
1126
- refetchOnReconnect: true,
1127
- refetchOnMount: false,
1128
- staleTime: Infinity,
1129
- retry: false
1130
- }
1131
- }
1132
- });
1133
- return queryClient;
1134
- }, []);
1135
- return React__default.createElement(QueryClientProvider, {
1136
- client: reactQueryClient
1137
- }, React__default.createElement(InternalServiceProvider, { ...props
1138
- }, children));
1139
- }
1140
-
1141
- function ReferenceEditor(props) {
1142
- return createElement(EntityProvider, {
1143
- sdk: props.sdk
1144
- }, createElement(FieldConnector, {
1145
- throttle: 0,
1146
- field: props.sdk.field,
1147
- isInitiallyDisabled: props.isInitiallyDisabled,
1148
- isEqualValues: (value1, value2) => {
1149
- return deepEqual(value1, value2);
1150
- }
1151
- }, props.children));
1152
- }
1153
- ReferenceEditor.defaultProps = {
1154
- isInitiallyDisabled: true,
1155
- hasCardEditActions: true
1156
- };
1157
-
1158
- function fromFieldValidations(field) {
1159
- var _field$items;
1160
-
1161
- // eslint-disable-next-line -- TODO: describe this disable @typescript-eslint/no-explicit-any
1162
- const validations = [...field.validations, ...(((_field$items = field.items) == null ? void 0 : _field$items.validations) ?? [])];
1163
- const linkContentTypeValidations = validations.find(v => 'linkContentType' in v);
1164
- const linkMimetypeGroupValidations = validations.find(v => 'linkMimetypeGroup' in v);
1165
- const sizeValidations = validations.find(v => 'size' in v);
1166
- const size = sizeValidations && sizeValidations.size || {};
1167
- const min = size.min;
1168
- const max = size.max;
1169
- let numberOfLinks = undefined;
1170
-
1171
- if (isNumber(min) && isNumber(max)) {
1172
- numberOfLinks = {
1173
- type: 'min-max',
1174
- min,
1175
- max
1176
- };
1177
- } else if (isNumber(min)) {
1178
- numberOfLinks = {
1179
- type: 'min',
1180
- min,
1181
- max: undefined
1182
- };
1183
- } else if (isNumber(max)) {
1184
- numberOfLinks = {
1185
- type: 'max',
1186
- max,
1187
- min: undefined
1188
- };
1189
- }
1190
-
1191
- const result = {
1192
- contentTypes: (linkContentTypeValidations == null ? void 0 : linkContentTypeValidations.linkContentType) ?? undefined,
1193
- mimetypeGroups: (linkMimetypeGroupValidations == null ? void 0 : linkMimetypeGroupValidations.linkMimetypeGroup) ?? undefined,
1194
- numberOfLinks // todo: there are multiple BE problems that need to be solved first, for now we don't want to apply size constraints
1195
- // linkedFileSize: findValidation(field, 'assetFileSize', {}),
1196
- // linkedImageDimensions: findValidation(field, 'assetImageDimensions', {})
1197
-
1198
- };
1199
- return result;
1200
- }
1201
-
1202
- const AllowActionsOnContentType = () => Promise.resolve(true);
1203
-
1204
- function useAccessApi(accessApi) {
1205
- const canPerformAction = accessApi.can;
1206
- const canPerformActionOnEntryOfType = accessApi.canPerformActionOnEntryOfType ?? AllowActionsOnContentType;
1207
- return {
1208
- canPerformAction,
1209
- canPerformActionOnEntryOfType
1210
- };
1211
- }
1212
-
1213
- async function filter(arr, predicate) {
1214
- // intentionally parallel as we assume it's cached in the implementation of the access api
1215
- const fail = Symbol();
1216
- const results = await Promise.all(arr.map(async item => (await predicate(item)) ? item : fail));
1217
- return results.filter(x => x !== fail);
1218
- }
1219
-
1220
- function useContentTypePermissions(props) {
1221
- const availableContentTypes = useMemo(() => {
1222
- if (props.entityType === 'Asset') {
1223
- return [];
1224
- }
1225
-
1226
- if (props.validations.contentTypes) {
1227
- return props.allContentTypes.filter(ct => {
1228
- var _props$validations$co;
1229
-
1230
- return (_props$validations$co = props.validations.contentTypes) == null ? void 0 : _props$validations$co.includes(ct.sys.id);
1231
- });
1232
- }
1233
-
1234
- return props.allContentTypes;
1235
- }, [props.allContentTypes, props.validations.contentTypes, props.entityType]);
1236
- const [creatableContentTypes, setCreatableContentTypes] = useState(availableContentTypes);
1237
- const [readableContentTypes, setReadableContentTypes] = useState(availableContentTypes);
1238
- const {
1239
- canPerformActionOnEntryOfType
1240
- } = useAccessApi(props.sdk.access);
1241
- useEffect(() => {
1242
- function getContentTypes(action) {
1243
- return filter(availableContentTypes, ct => canPerformActionOnEntryOfType(action, ct.sys.id));
1244
- }
1245
-
1246
- async function checkContentTypeAccess() {
1247
- const creatable = await getContentTypes('create');
1248
- const readable = await getContentTypes('read');
1249
- setCreatableContentTypes(creatable);
1250
- setReadableContentTypes(readable);
1251
- }
1252
-
1253
- void checkContentTypeAccess(); // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1254
- }, [availableContentTypes]);
1255
- return {
1256
- creatableContentTypes,
1257
- readableContentTypes,
1258
- availableContentTypes
1259
- };
1260
- }
1261
-
1262
- function useEditorPermissions(props) {
1263
- const {
1264
- sdk,
1265
- entityType,
1266
- parameters
1267
- } = props;
1268
- const validations = useMemo(() => fromFieldValidations(props.sdk.field), [props.sdk.field]);
1269
- const [canCreateEntity, setCanCreateEntity] = useState(true);
1270
- const [canLinkEntity, setCanLinkEntity] = useState(true);
1271
- const {
1272
- creatableContentTypes,
1273
- readableContentTypes,
1274
- availableContentTypes
1275
- } = useContentTypePermissions({ ...props,
1276
- validations
1277
- });
1278
- const {
1279
- canPerformAction
1280
- } = useAccessApi(sdk.access);
1281
- useEffect(() => {
1282
- if (parameters.instance.showCreateEntityAction === false) {
1283
- setCanCreateEntity(false);
1284
- return;
1285
- }
1286
-
1287
- async function checkCreateAccess() {
1288
- if (entityType === 'Asset') {
1289
- // Hardcoded `true` value following https://contentful.atlassian.net/browse/DANTE-486
1290
- // TODO: refine permissions check in order to account for tags in rules
1291
- const canCreate = (await canPerformAction('create', 'Asset')) || true;
1292
- setCanCreateEntity(canCreate);
1293
- }
1294
-
1295
- if (entityType === 'Entry') {
1296
- // Hardcoded `true` value following https://contentful.atlassian.net/browse/DANTE-486
1297
- // TODO: refine permissions check in order to account for tags in rules
1298
- const canCreate = creatableContentTypes.length > 0 || true;
1299
- setCanCreateEntity(canCreate);
1300
- }
1301
- }
1302
-
1303
- void checkCreateAccess(); // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1304
- }, [entityType, parameters.instance, creatableContentTypes]);
1305
- useEffect(() => {
1306
- if (parameters.instance.showLinkEntityAction === false) {
1307
- setCanLinkEntity(false);
1308
- return;
1309
- }
1310
-
1311
- async function checkLinkAccess() {
1312
- if (entityType === 'Asset') {
1313
- // Hardcoded `true` value following https://contentful.atlassian.net/browse/DANTE-486
1314
- // TODO: refine permissions check in order to account for tags in rules
1315
- const canRead = (await canPerformAction('read', 'Asset')) || true;
1316
- setCanLinkEntity(canRead);
1317
- }
1318
-
1319
- if (entityType === 'Entry') {
1320
- // Hardcoded `true` value following https://contentful.atlassian.net/browse/DANTE-486
1321
- // TODO: refine permissions check in order to account for tags in rules
1322
- // TODO: always show every content type (it's just a filter) to avoid people not seeing
1323
- // their (partly limited) content types
1324
- const canRead = true;
1325
- setCanLinkEntity(canRead);
1326
- }
1327
- }
1328
-
1329
- void checkLinkAccess(); // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1330
- }, [entityType, parameters.instance, readableContentTypes]);
1331
- return {
1332
- canCreateEntity,
1333
- canLinkEntity,
1334
- creatableContentTypes,
1335
- readableContentTypes,
1336
- availableContentTypes,
1337
- validations
1338
- };
1339
- }
1340
-
1341
- function Editor(props) {
1342
- const {
1343
- setValue,
1344
- entityType
1345
- } = props;
1346
- const editorPermissions = useEditorPermissions(props);
1347
- const onCreate = useCallback(id => void setValue({
1348
- sys: {
1349
- type: 'Link',
1350
- linkType: entityType,
1351
- id
1352
- }
1353
- }), [setValue, entityType]);
1354
- const onLink = useCallback(ids => {
1355
- const [id] = ids;
1356
- setValue({
1357
- sys: {
1358
- type: 'Link',
1359
- linkType: entityType,
1360
- id
1361
- }
1362
- });
1363
- }, [setValue, entityType]);
1364
- const linkActionsProps = useLinkActionsProps({ ...props,
1365
- canLinkMultiple: false,
1366
- editorPermissions,
1367
- onCreate,
1368
- onLink
1369
- }); // Inject card actions props into the given custom card renderer
1370
-
1371
- const customCardRenderer = useCallback((cardProps, _, renderDefaultCard) => props.renderCustomCard ? props.renderCustomCard(cardProps, linkActionsProps, renderDefaultCard) : false, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1372
- [linkActionsProps]);
1373
-
1374
- if (!props.entityId) {
1375
- return createElement(LinkEntityActions, {
1376
- renderCustomActions: props.renderCustomActions,
1377
- ...linkActionsProps
1378
- });
1379
- }
1380
-
1381
- return props.children({ ...props,
1382
- renderCustomCard: props.renderCustomCard && customCardRenderer
1383
- });
1384
- }
1385
-
1386
- function SingleReferenceEditor(props) {
1387
- const allContentTypes = props.sdk.space.getCachedContentTypes();
1388
- return createElement(ReferenceEditor, { ...props
1389
- }, ({
1390
- value,
1391
- setValue,
1392
- disabled,
1393
- externalReset
1394
- }) => {
1395
- return createElement(Editor, { ...props,
1396
- key: `${externalReset}-reference`,
1397
- entityId: value ? value.sys.id : '',
1398
- isDisabled: disabled,
1399
- setValue: setValue,
1400
- allContentTypes: allContentTypes
1401
- });
1402
- });
1403
- }
1404
- SingleReferenceEditor.defaultProps = {
1405
- hasCardEditActions: true,
1406
- hasCardRemoveActions: true
1407
- };
1408
-
1409
- const styles$1 = {
1410
- spaceIcon: /*#__PURE__*/css({
1411
- flexShrink: 0,
1412
- fill: tokens.purple600
1413
- }),
1414
- spaceName: /*#__PURE__*/css({
1415
- color: tokens.gray700,
1416
- fontSize: tokens.fontSizeS,
1417
- fontWeight: tokens.fontWeightDemiBold,
1418
- maxWidth: '80px',
1419
- textOverflow: 'ellipsis',
1420
- overflow: 'hidden',
1421
- whiteSpace: 'nowrap'
1422
- })
1423
- };
1424
- function SpaceName(props) {
1425
- return createElement(Tooltip, {
1426
- placement: "top",
1427
- as: "div",
1428
- content: `Space: ${props.spaceName}`
1429
- }, createElement(Flex, {
1430
- alignItems: "center",
1431
- gap: "spacingXs",
1432
- marginRight: "spacingS"
1433
- }, createElement(FolderOpenTrimmedIcon, {
1434
- className: styles$1.spaceIcon,
1435
- size: "tiny",
1436
- "aria-label": "Source space"
1437
- }), createElement(Text, {
1438
- className: styles$1.spaceName
1439
- }, props.spaceName)));
1440
- }
1441
-
1442
- const {
1443
- getEntryTitle,
1444
- getEntityDescription,
1445
- getEntryStatus,
1446
- getEntryImage
1447
- } = entityHelpers;
1448
- const styles$2 = {
1449
- scheduleIcon: /*#__PURE__*/css({
1450
- marginRight: tokens.spacing2Xs
1451
- })
1452
- };
1453
- const defaultProps = {
1454
- isClickable: true,
1455
- hasCardEditActions: true,
1456
- hasCardMoveActions: true,
1457
- hasCardRemoveActions: true
1458
- };
1459
- function WrappedEntryCard(props) {
1460
- var _props$entry;
1461
-
1462
- const [file, setFile] = useState(null);
1463
- const {
1464
- contentType
1465
- } = props;
1466
- useEffect(() => {
1467
- if (props.entry) {
1468
- getEntryImage({
1469
- entry: props.entry,
1470
- contentType,
1471
- localeCode: props.localeCode,
1472
- defaultLocaleCode: props.defaultLocaleCode
1473
- }, props.getAsset).then(file => {
1474
- setFile(file);
1475
- }).catch(() => {
1476
- setFile(null);
1477
- });
1478
- }
1479
- }, [props.entry, props.getAsset, contentType, props.localeCode, props.defaultLocaleCode]);
1480
- const status = getEntryStatus((_props$entry = props.entry) == null ? void 0 : _props$entry.sys);
1481
-
1482
- if (status === 'deleted') {
1483
- return createElement(MissingEntityCard, {
1484
- entityType: "Entry",
1485
- isDisabled: props.isDisabled,
1486
- onRemove: props.onRemove
1487
- });
1488
- }
1489
-
1490
- const title = getEntryTitle({
1491
- entry: props.entry,
1492
- contentType,
1493
- localeCode: props.localeCode,
1494
- defaultLocaleCode: props.defaultLocaleCode,
1495
- defaultTitle: 'Untitled'
1496
- });
1497
- const description = getEntityDescription({
1498
- entity: props.entry,
1499
- contentType,
1500
- localeCode: props.localeCode,
1501
- defaultLocaleCode: props.defaultLocaleCode
1502
- });
1503
- return createElement(EntryCard, {
1504
- as: props.entryUrl ? 'a' : 'article',
1505
- href: props.entryUrl,
1506
- title: title,
1507
- description: description,
1508
- contentType: contentType == null ? void 0 : contentType.name,
1509
- size: props.size,
1510
- isSelected: props.isSelected,
1511
- status: status,
1512
- icon: props.spaceName ? createElement(SpaceName, {
1513
- spaceName: props.spaceName
1514
- }) : createElement(ScheduledIconWithTooltip, {
1515
- getEntityScheduledActions: props.getEntityScheduledActions,
1516
- entityType: "Entry",
1517
- entityId: props.entry.sys.id
1518
- }, createElement(ClockIcon, {
1519
- className: styles$2.scheduleIcon,
1520
- size: "small",
1521
- variant: "muted",
1522
- testId: "schedule-icon"
1523
- })),
1524
- thumbnailElement: file && isValidImage(file) ? createElement(AssetThumbnail, {
1525
- file: file
1526
- }) : undefined,
1527
- dragHandleRender: props.renderDragHandle,
1528
- withDragHandle: !!props.renderDragHandle,
1529
- actions: props.onEdit || props.onRemove ? [props.hasCardEditActions && props.onEdit ? createElement(MenuItem, {
1530
- key: "edit",
1531
- testId: "edit",
1532
- onClick: () => {
1533
- props.onEdit && props.onEdit();
1534
- }
1535
- }, "Edit") : null, props.hasCardRemoveActions && props.onRemove ? createElement(MenuItem, {
1536
- key: "delete",
1537
- testId: "delete",
1538
- onClick: () => {
1539
- props.onRemove && props.onRemove();
1540
- }
1541
- }, "Remove") : null, props.hasCardMoveActions && (props.onMoveTop || props.onMoveBottom) ? createElement(MenuDivider, {
1542
- key: "divider"
1543
- }) : null, props.hasCardMoveActions && props.onMoveTop ? createElement(MenuItem, {
1544
- key: "move-top",
1545
- onClick: () => props.onMoveTop && props.onMoveTop(),
1546
- testId: "move-top"
1547
- }, "Move to top") : null, props.hasCardMoveActions && props.onMoveBottom ? createElement(MenuItem, {
1548
- key: "move-bottom",
1549
- onClick: () => props.onMoveBottom && props.onMoveBottom(),
1550
- testId: "move-bottom"
1551
- }, "Move to bottom") : null].filter(item => item) : [],
1552
- onClick: // Providing an onClick handler messes up with some rich text
1553
- // features e.g. pressing ENTER on a card to add a new paragraph
1554
- // underneath. It's crucial not to pass a custom handler when
1555
- // isClickable is disabled which in the case of RT it's.
1556
- props.isClickable ? e => {
1557
- e.preventDefault();
1558
- if (props.onClick) return props.onClick(e);
1559
- props.onEdit && props.onEdit();
1560
- } : undefined
1561
- });
1562
- }
1563
- WrappedEntryCard.defaultProps = defaultProps;
1564
-
1565
- async function openEntry(sdk, entryId, options) {
1566
- let slide;
1567
-
1568
- if (options.bulkEditing) {
1569
- try {
1570
- const result = await sdk.navigator.openBulkEditor(sdk.entry.getSys().id, {
1571
- fieldId: sdk.field.id,
1572
- locale: sdk.field.locale,
1573
- index: options.index ?? 0
1574
- });
1575
- slide = result.slide;
1576
- return slide;
1577
- } catch (e) {// we don't allow to open multiple bulk editors for performance reasons
1578
- // proceed with a default openEntry
1579
- }
1580
- }
1581
-
1582
- const result = await sdk.navigator.openEntry(entryId, {
1583
- slideIn: true
1584
- });
1585
- slide = result.slide;
1586
- return slide;
1587
- }
1588
-
1589
- function FetchingWrappedEntryCard(props) {
1590
- const {
1591
- data: entry,
1592
- status
1593
- } = useEntity('Entry', props.entryId);
1594
- const {
1595
- getEntityScheduledActions
1596
- } = useEntityLoader();
1597
- const loadEntityScheduledActions = useCallback(() => getEntityScheduledActions('Entry', props.entryId), [getEntityScheduledActions, props.entryId]);
1598
- const size = props.viewType === 'link' ? 'small' : 'default';
1599
- const {
1600
- getEntity
1601
- } = useEntityLoader();
1602
-
1603
- const getAsset = assetId => getEntity('Asset', assetId);
1604
-
1605
- const onEdit = async () => {
1606
- const slide = await openEntry(props.sdk, props.entryId, {
1607
- bulkEditing: props.parameters.instance.bulkEditing,
1608
- index: props.index
1609
- });
1610
- props.onAction && props.onAction({
1611
- entity: 'Entry',
1612
- type: 'edit',
1613
- id: props.entryId,
1614
- contentTypeId: get(entry, 'sys.contentType.sys.id'),
1615
- slide
1616
- });
1617
- };
1618
-
1619
- const onRemoveEntry = () => {
1620
- props.onRemove();
1621
- props.onAction && props.onAction({
1622
- entity: 'Entry',
1623
- type: 'delete',
1624
- id: props.entryId,
1625
- contentTypeId: get(entry, 'sys.contentType.sys.id')
1626
- });
1627
- };
1628
-
1629
- useEffect(() => {
1630
- if (entry) {
1631
- props.onAction && props.onAction({
1632
- type: 'rendered',
1633
- entity: 'Entry'
1634
- });
1635
- } // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1636
-
1637
- }, [entry]);
1638
- return useMemo(() => {
1639
- if (status === 'error') {
1640
- const card = createElement(MissingEntityCard, {
1641
- entityType: "Entry",
1642
- isDisabled: props.isDisabled,
1643
- onRemove: onRemoveEntry
1644
- });
1645
-
1646
- if (props.renderCustomMissingEntityCard) {
1647
- return props.renderCustomMissingEntityCard({
1648
- defaultCard: card,
1649
- entity: {
1650
- id: props.entryId,
1651
- type: 'Entry'
1652
- }
1653
- });
1654
- }
1655
-
1656
- return card;
1657
- }
1658
-
1659
- if (status === 'loading') {
1660
- return createElement(EntryCard, {
1661
- size: size,
1662
- isLoading: true
1663
- });
1664
- }
1665
-
1666
- const sharedCardProps = {
1667
- index: props.index,
1668
- entity: entry,
1669
- entityUrl: props.getEntityUrl && props.getEntityUrl(entry.sys.id),
1670
- contentType: props.allContentTypes.find(contentType => contentType.sys.id === entry.sys.contentType.sys.id),
1671
- isDisabled: props.isDisabled,
1672
- size,
1673
- localeCode: props.sdk.field.locale,
1674
- defaultLocaleCode: props.sdk.locales.default,
1675
- renderDragHandle: props.renderDragHandle,
1676
- onEdit,
1677
- onRemove: onRemoveEntry,
1678
- onMoveTop: props.onMoveTop,
1679
- onMoveBottom: props.onMoveBottom,
1680
- isBeingDragged: props.isBeingDragged
1681
- };
1682
- const {
1683
- hasCardEditActions,
1684
- hasCardMoveActions,
1685
- hasCardRemoveActions
1686
- } = props;
1687
-
1688
- function renderDefaultCard(props) {
1689
- const builtinCardProps = { ...sharedCardProps,
1690
- ...props,
1691
- hasCardEditActions,
1692
- hasCardMoveActions,
1693
- hasCardRemoveActions,
1694
- getAsset,
1695
- getEntityScheduledActions: loadEntityScheduledActions,
1696
- entry: (props == null ? void 0 : props.entity) || sharedCardProps.entity,
1697
- entryUrl: (props == null ? void 0 : props.entityUrl) || sharedCardProps.entityUrl
1698
- };
1699
- return createElement(WrappedEntryCard, { ...builtinCardProps
1700
- });
1701
- }
1702
-
1703
- if (props.renderCustomCard) {
1704
- // LinkActionsProps are injected higher SingleReferenceEditor/MultipleReferenceEditor
1705
- const renderedCustomCard = props.renderCustomCard(sharedCardProps, {}, renderDefaultCard); // Only `false` indicates to render the original card. E.g. `null` would result in no card.
1706
-
1707
- if (renderedCustomCard !== false) {
1708
- return renderedCustomCard;
1709
- }
1710
- }
1711
-
1712
- return renderDefaultCard(); // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1713
- }, [props, status, entry]);
1714
- }
1715
-
1716
- function SingleEntryReferenceEditor(props) {
1717
- return createElement(SingleReferenceEditor, { ...props,
1718
- entityType: "Entry"
1719
- }, ({
1720
- allContentTypes,
1721
- isDisabled,
1722
- entityId,
1723
- setValue,
1724
- renderCustomCard,
1725
- hasCardRemoveActions,
1726
- hasCardEditActions
1727
- }) => {
1728
- return createElement(FetchingWrappedEntryCard, { ...props,
1729
- allContentTypes: allContentTypes,
1730
- isDisabled: isDisabled,
1731
- entryId: entityId,
1732
- renderCustomCard: renderCustomCard,
1733
- hasCardEditActions: hasCardEditActions,
1734
- hasCardRemoveActions: hasCardRemoveActions,
1735
- onRemove: () => {
1736
- setValue(null);
1737
- }
1738
- });
1739
- });
1740
- }
1741
- SingleEntryReferenceEditor.defaultProps = {
1742
- isInitiallyDisabled: true
1743
- };
1744
-
1745
- function onLinkOrCreate(setValue, entityType, items, ids, index = items.length) {
1746
- const links = ids.map(id => ({
1747
- sys: {
1748
- type: 'Link',
1749
- linkType: entityType,
1750
- id
1751
- }
1752
- }));
1753
- const newItems = Array.from(items);
1754
- newItems.splice(index, 0, ...links);
1755
- setValue(newItems);
1756
- }
1757
-
1758
- const emptyArray = [];
1759
- const nullableValue = {
1760
- sys: {
1761
- id: 'null-value'
1762
- }
1763
- };
1764
-
1765
- function Editor$1(props) {
1766
- const {
1767
- setValue,
1768
- entityType,
1769
- setIndexToUpdate
1770
- } = props;
1771
- const editorPermissions = useEditorPermissions(props);
1772
- const items = useMemo(() => {
1773
- return (props.items || []). // If null values have found their way into the persisted
1774
- // value for the multiref field, replace them with an object
1775
- // that has the shape of a Link to make the missing entry/asset
1776
- // card render
1777
- map(link => link || nullableValue);
1778
- }, [props.items]);
1779
- const onSortStart = useCallback((_, event) => {
1780
- if (event instanceof MouseEvent) {
1781
- document.body.classList.add('grabbing');
1782
- }
1783
-
1784
- event.preventDefault();
1785
- }, []);
1786
- const onSortEnd = useCallback(({
1787
- oldIndex,
1788
- newIndex
1789
- }) => {
1790
- // custom callback that is invoked *before* we sort the array
1791
- // e.g. in Compose we want to sort the references in the referenceMap before re-rendering drag and drop
1792
- props.onSortingEnd && props.onSortingEnd({
1793
- oldIndex,
1794
- newIndex
1795
- });
1796
- const newItems = arrayMove(items, oldIndex, newIndex);
1797
- setValue(newItems);
1798
- setIndexToUpdate && setIndexToUpdate(undefined);
1799
- document.body.classList.remove('grabbing');
1800
- }, [items, props, setIndexToUpdate, setValue]);
1801
- const onMove = useCallback((oldIndex, newIndex) => {
1802
- const newItems = arrayMove(items, oldIndex, newIndex);
1803
- setValue(newItems);
1804
- }, [items, setValue]);
1805
- const onCreate = useCallback((id, index) => onLinkOrCreate(setValue, entityType, items, [id], index), [setValue, items, entityType]);
1806
- const onLink = useCallback((ids, index) => onLinkOrCreate(setValue, entityType, items, ids, index), [setValue, items, entityType]);
1807
- const linkActionsProps = useLinkActionsProps({ ...props,
1808
- canLinkMultiple: true,
1809
- editorPermissions,
1810
- onCreate,
1811
- onLink,
1812
- itemsLength: items.length
1813
- });
1814
- const customCardRenderer = useCallback((cardProps, _, renderDefaultCard) => props.renderCustomCard ? props.renderCustomCard(cardProps, linkActionsProps, renderDefaultCard) : false, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
1815
- [linkActionsProps]);
1816
- return createElement(Fragment, null, props.children({ ...props,
1817
- onSortStart: onSortStart,
1818
- onSortEnd: onSortEnd,
1819
- onMove,
1820
- renderCustomCard: props.renderCustomCard && customCardRenderer
1821
- }), createElement(LinkEntityActions, {
1822
- renderCustomActions: props.renderCustomActions,
1823
- ...linkActionsProps
1824
- }));
1825
- }
1826
-
1827
- function MultipleReferenceEditor(props) {
1828
- const allContentTypes = props.sdk.space.getCachedContentTypes();
1829
- return createElement(ReferenceEditor, { ...props
1830
- }, ({
1831
- value,
1832
- disabled,
1833
- setValue,
1834
- externalReset
1835
- }) => {
1836
- return createElement(Editor$1, { ...props,
1837
- items: value || emptyArray,
1838
- isDisabled: disabled,
1839
- setValue: setValue,
1840
- key: `${externalReset}-list`,
1841
- allContentTypes: allContentTypes
1842
- });
1843
- });
1844
- }
1845
- MultipleReferenceEditor.defaultProps = {
1846
- hasCardEditActions: true
1847
- };
1848
-
1849
- const styles$3 = {
1850
- container: /*#__PURE__*/css({
1851
- position: 'relative'
1852
- }),
1853
- item: /*#__PURE__*/css({
1854
- marginBottom: tokens.spacingM,
1855
- zIndex: tokens.zIndexModal // setting this to an index above 99 fixes dragged item disappearing issue. Should not be higher than 100 so it does not overlap the asset modal.
1856
-
1857
- })
1858
- };
1859
-
1860
- const DragHandle = props => {
1861
- const SortableDragHandle = SortableHandle(() => props.drag);
1862
- return React__default.createElement(SortableDragHandle, null);
1863
- };
1864
-
1865
- const SortableLink = /*#__PURE__*/SortableElement(props => React__default.createElement("div", {
1866
- className: styles$3.item
1867
- }, props.children));
1868
- const SortableLinkListInternal = /*#__PURE__*/SortableContainer(props => {
1869
- return React__default.createElement("div", {
1870
- className: cx(styles$3.container, props.className)
1871
- }, props.items.map((item, index) => React__default.createElement(SortableLink, {
1872
- disabled: props.isDisabled,
1873
- key: `${item.sys.urn ?? item.sys.id}-${index}`,
1874
- index: index
1875
- }, props.children({
1876
- items: props.items,
1877
- isDisabled: props.isDisabled,
1878
- item,
1879
- index,
1880
- DragHandle: props.isDisabled ? undefined : DragHandle
1881
- }))));
1882
- }); // HOC does not support generics, so we mimic it via additional component
1883
-
1884
- function SortableLinkList(props) {
1885
- // with the default distance of 0 the drag start event is "confused" with the click event,
1886
- // so the latter one isn't fired and click handlers on child elements don't work
1887
- return React__default.createElement(SortableLinkListInternal, {
1888
- distance: 1,
1889
- ...props
1890
- }, props.children);
1891
- }
1892
-
1893
- function MultipleEntryReferenceEditor(props) {
1894
- const [indexToUpdate, setIndexToUpdate] = useState(undefined);
1895
-
1896
- const updateBeforeSortStart = ({
1897
- index
1898
- }) => {
1899
- setIndexToUpdate(index);
1900
- };
1901
-
1902
- return createElement(MultipleReferenceEditor, { ...props,
1903
- entityType: "Entry",
1904
- setIndexToUpdate: setIndexToUpdate
1905
- }, childrenProps => createElement(SortableLinkList, { ...childrenProps,
1906
- axis: "y",
1907
- useDragHandle: true,
1908
- updateBeforeSortStart: updateBeforeSortStart
1909
- }, ({
1910
- items,
1911
- item,
1912
- index,
1913
- isDisabled,
1914
- DragHandle
1915
- }) => {
1916
- const lastIndex = items.length - 1;
1917
- return createElement(FetchingWrappedEntryCard, { ...childrenProps,
1918
- key: `${item.sys.id}-${index}`,
1919
- index: index,
1920
- allContentTypes: childrenProps.allContentTypes,
1921
- isDisabled: isDisabled,
1922
- entryId: item.sys.id,
1923
- onRemove: () => {
1924
- childrenProps.setValue(items.filter((_value, i) => i !== index));
1925
- },
1926
- onMoveTop: index !== 0 ? () => childrenProps.onMove(index, 0) : undefined,
1927
- onMoveBottom: index !== lastIndex ? () => childrenProps.onMove(index, lastIndex) : undefined,
1928
- renderDragHandle: DragHandle,
1929
- isBeingDragged: index === indexToUpdate
1930
- });
1931
- }));
1932
- }
1933
-
1934
- function downloadAsset(url) {
1935
- window.open(url, '_blank', 'noopener,noreferrer');
1936
- }
1937
-
1938
- function renderAssetInfo(props) {
1939
- const {
1940
- entityFile
1941
- } = props;
1942
- const fileName = get(entityFile, 'fileName');
1943
- const mimeType = get(entityFile, 'contentType');
1944
- const fileSize = get(entityFile, 'details.size');
1945
- const image = get(entityFile, 'details.image');
1946
- return [React__default.createElement(Menu.SectionTitle, {
1947
- key: "file-section"
1948
- }, "File info"), fileName && React__default.createElement(Menu.Item, {
1949
- key: "file-name"
1950
- }, React__default.createElement(Text, {
1951
- isTruncated: true
1952
- }, fileName)), mimeType && React__default.createElement(Menu.Item, {
1953
- key: "file-type"
1954
- }, React__default.createElement(Text, {
1955
- isTruncated: true
1956
- }, mimeType)), fileSize && React__default.createElement(Menu.Item, {
1957
- key: "file-size"
1958
- }, shortenStorageUnit(fileSize, 'B')), image && React__default.createElement(Menu.Item, {
1959
- key: "file-dimentions"
1960
- }, `${image.width} × ${image.height}`)].filter(item => item);
1961
- }
1962
- function renderActions(props) {
1963
- const {
1964
- entityFile,
1965
- isDisabled,
1966
- onEdit,
1967
- onRemove
1968
- } = props;
1969
- return [React__default.createElement(Menu.SectionTitle, {
1970
- key: "section-title"
1971
- }, "Actions"), onEdit ? React__default.createElement(Menu.Item, {
1972
- key: "edit",
1973
- onClick: onEdit,
1974
- testId: "card-action-edit"
1975
- }, "Edit") : null, entityFile ? React__default.createElement(Menu.Item, {
1976
- key: "download",
1977
- onClick: () => {
1978
- if (typeof entityFile.url === 'string') {
1979
- downloadAsset(entityFile.url);
1980
- }
1981
- },
1982
- testId: "card-action-download"
1983
- }, "Download") : null, onRemove ? React__default.createElement(Menu.Item, {
1984
- key: "remove",
1985
- disabled: isDisabled,
1986
- onClick: onRemove,
1987
- testId: "card-action-remove"
1988
- }, "Remove") : null].filter(item => item);
1989
- }
1990
-
1991
- const groupToIconMap = {
1992
- image: 'image',
1993
- video: 'video',
1994
- audio: 'audio',
1995
- richtext: 'richtext',
1996
- presentation: 'presentation',
1997
- spreadsheet: 'spreadsheet',
1998
- pdfdocument: 'pdf',
1999
- archive: 'archive',
2000
- plaintext: 'plaintext',
2001
- code: 'code',
2002
- markup: 'markup'
2003
- };
2004
- const styles$4 = {
2005
- scheduleIcon: /*#__PURE__*/css({
2006
- marginRight: tokens.spacing2Xs
2007
- })
2008
- };
2009
- const defaultProps$1 = {
2010
- isClickable: true
2011
- }; // eslint-disable-next-line -- TODO: describe this disable @typescript-eslint/no-explicit-any
2012
-
2013
- function getFileType(file) {
2014
- if (!file) {
2015
- return 'archive';
2016
- }
2017
-
2018
- const groupName = mimetype.getGroupLabel({
2019
- type: file.contentType,
2020
- fallbackFileName: file.fileName
2021
- });
2022
- return groupToIconMap[groupName] || 'archive';
2023
- }
2024
-
2025
- const WrappedAssetCard = props => {
2026
- const {
2027
- className,
2028
- onEdit,
2029
- getAssetUrl,
2030
- onRemove,
2031
- size,
2032
- isDisabled,
2033
- isSelected,
2034
- isClickable
2035
- } = props;
2036
- const status = entityHelpers.getEntryStatus(props.asset.sys);
2037
-
2038
- if (status === 'deleted') {
2039
- return React__default.createElement(MissingEntityCard, {
2040
- entityType: "Asset",
2041
- asSquare: true,
2042
- isDisabled: props.isDisabled,
2043
- onRemove: props.onRemove
2044
- });
2045
- }
2046
-
2047
- const entityTitle = entityHelpers.getAssetTitle({
2048
- asset: props.asset,
2049
- localeCode: props.localeCode,
2050
- defaultLocaleCode: props.defaultLocaleCode,
2051
- defaultTitle: 'Untitled'
2052
- });
2053
- const entityFile = props.asset.fields.file ? props.asset.fields.file[props.localeCode] || props.asset.fields.file[props.defaultLocaleCode] : undefined;
2054
- const href = getAssetUrl ? getAssetUrl(props.asset.sys.id) : undefined;
2055
- return React__default.createElement(AssetCard, {
2056
- as: href ? 'a' : 'article',
2057
- type: getFileType(entityFile),
2058
- title: entityTitle,
2059
- className: className,
2060
- isSelected: isSelected,
2061
- href: href,
2062
- status: status,
2063
- icon: React__default.createElement(ScheduledIconWithTooltip, {
2064
- getEntityScheduledActions: props.getEntityScheduledActions,
2065
- entityType: "Asset",
2066
- entityId: props.asset.sys.id
2067
- }, React__default.createElement(ClockIcon, {
2068
- className: styles$4.scheduleIcon,
2069
- size: "small",
2070
- variant: "muted",
2071
- testId: "schedule-icon"
2072
- })),
2073
- src: entityFile && entityFile.url ? size === 'small' ? `${entityFile.url}?w=150&h=150&fit=thumb` : `${entityFile.url}?h=300` : '',
2074
- onClick: // Providing an onClick handler messes up with some rich text
2075
- // features e.g. pressing ENTER on a card to add a new paragraph
2076
- // underneath. It's crucial not to pass a custom handler when
2077
- // isClickable is disabled which in the case of RT it's.
2078
- isClickable ? e => {
2079
- e.preventDefault();
2080
- onEdit && onEdit();
2081
- } : undefined,
2082
-
2083
- /* todo - remove this when onKeyDown is allowed as a prop for BaseCard in forma 36
2084
- // @ts-expect-error */
2085
- onKeyDown: isClickable ? e => {
2086
- if (e.key === 'Enter' && onEdit) {
2087
- e.preventDefault();
2088
- onEdit();
2089
- }
2090
- } : undefined,
2091
- dragHandleRender: props.renderDragHandle,
2092
- withDragHandle: !!props.renderDragHandle,
2093
- actions: [...renderActions({
2094
- entityFile,
2095
- isDisabled: isDisabled,
2096
- onEdit,
2097
- onRemove
2098
- }), ...(entityFile ? renderAssetInfo({
2099
- entityFile
2100
- }) : [])].filter(item => item),
2101
- size: size
2102
- });
2103
- };
2104
- WrappedAssetCard.defaultProps = defaultProps$1;
2105
-
2106
- const styles$5 = {
2107
- scheduleIcon: /*#__PURE__*/css({
2108
- marginRight: tokens.spacing2Xs
2109
- })
2110
- };
2111
- const WrappedAssetLink = props => {
2112
- const {
2113
- className,
2114
- href,
2115
- onEdit,
2116
- onRemove,
2117
- isDisabled
2118
- } = props;
2119
- const status = entityHelpers.getEntryStatus(props.asset.sys);
2120
-
2121
- if (status === 'deleted') {
2122
- return React__default.createElement(MissingEntityCard, {
2123
- entityType: "Asset",
2124
- isDisabled: props.isDisabled,
2125
- onRemove: props.onRemove
2126
- });
2127
- }
2128
-
2129
- const entityTitle = entityHelpers.getAssetTitle({
2130
- asset: props.asset,
2131
- localeCode: props.localeCode,
2132
- defaultLocaleCode: props.defaultLocaleCode,
2133
- defaultTitle: 'Untitled'
2134
- });
2135
- const entityFile = props.asset.fields.file ? props.asset.fields.file[props.localeCode] || props.asset.fields.file[props.defaultLocaleCode] : undefined;
2136
- return React__default.createElement(EntryCard, {
2137
- as: href ? 'a' : 'article',
2138
- contentType: "Asset",
2139
- title: entityTitle,
2140
- className: className,
2141
- href: href,
2142
- size: "small",
2143
- status: status,
2144
- thumbnailElement: entityFile && isValidImage(entityFile) ? React__default.createElement(AssetThumbnail, {
2145
- file: entityFile
2146
- }) : undefined,
2147
- icon: React__default.createElement(ScheduledIconWithTooltip, {
2148
- getEntityScheduledActions: props.getEntityScheduledActions,
2149
- entityType: "Asset",
2150
- entityId: props.asset.sys.id
2151
- }, React__default.createElement(ClockIcon, {
2152
- className: styles$5.scheduleIcon,
2153
- size: "small",
2154
- variant: "muted",
2155
- testId: "schedule-icon"
2156
- })),
2157
- onClick: e => {
2158
- e.preventDefault();
2159
- onEdit();
2160
- },
2161
- onKeyDown: e => {
2162
- if (e.key === 'Enter' && onEdit) {
2163
- e.preventDefault();
2164
- onEdit();
2165
- }
2166
- },
2167
- dragHandleRender: props.renderDragHandle,
2168
- withDragHandle: !!props.renderDragHandle,
2169
- actions: [renderActions({
2170
- entityFile,
2171
- isDisabled: isDisabled,
2172
- onEdit,
2173
- onRemove
2174
- }), entityFile ? renderAssetInfo({
2175
- entityFile
2176
- }) : null].filter(item => item)
2177
- });
2178
- };
2179
-
2180
- function FetchingWrappedAssetCard(props) {
2181
- const {
2182
- data: asset,
2183
- status
2184
- } = useEntity('Asset', props.assetId);
2185
- const {
2186
- getEntityScheduledActions
2187
- } = useEntityLoader();
2188
- const loadEntityScheduledActions = useCallback(() => getEntityScheduledActions('Asset', props.assetId), [getEntityScheduledActions, props.assetId]);
2189
- useEffect(() => {
2190
- if (asset) {
2191
- props.onAction && props.onAction({
2192
- type: 'rendered',
2193
- entity: 'Asset'
2194
- });
2195
- } // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
2196
-
2197
- }, [asset]);
2198
-
2199
- const onEdit = async () => {
2200
- const {
2201
- slide
2202
- } = await props.sdk.navigator.openAsset(props.assetId, {
2203
- slideIn: true
2204
- });
2205
- props.onAction && props.onAction({
2206
- entity: 'Asset',
2207
- type: 'edit',
2208
- id: props.assetId,
2209
- contentTypeId: '',
2210
- slide
2211
- });
2212
- };
2213
-
2214
- const onRemove = () => {
2215
- props.onRemove();
2216
- props.onAction && props.onAction({
2217
- entity: 'Asset',
2218
- type: 'delete',
2219
- id: props.assetId,
2220
- contentTypeId: ''
2221
- });
2222
- };
2223
-
2224
- return useMemo(() => {
2225
- if (status === 'error') {
2226
- const card = createElement(MissingEntityCard, {
2227
- entityType: "Asset",
2228
- asSquare: props.viewType !== 'link',
2229
- isDisabled: props.isDisabled,
2230
- onRemove: onRemove
2231
- });
2232
-
2233
- if (props.renderCustomMissingEntityCard) {
2234
- return props.renderCustomMissingEntityCard({
2235
- defaultCard: card,
2236
- entity: {
2237
- id: props.assetId,
2238
- type: 'Asset'
2239
- }
2240
- });
2241
- }
2242
-
2243
- return card;
2244
- }
2245
-
2246
- const {
2247
- getEntityUrl
2248
- } = props;
2249
- const size = props.viewType === 'big_card' ? 'default' : 'small';
2250
- const commonProps = {
2251
- asset,
2252
- entityUrl: getEntityUrl && getEntityUrl(props.assetId),
2253
- size: size,
2254
- isDisabled: props.isDisabled,
2255
- localeCode: props.sdk.field.locale,
2256
- defaultLocaleCode: props.sdk.locales.default,
2257
- renderDragHandle: props.renderDragHandle,
2258
- onEdit,
2259
- onRemove
2260
- };
2261
-
2262
- if (props.viewType === 'link') {
2263
- if (status === 'loading') {
2264
- return createElement(EntryCard, {
2265
- size: "small",
2266
- isLoading: true
2267
- });
2268
- }
2269
-
2270
- return createElement(WrappedAssetLink, { ...commonProps,
2271
- href: commonProps.entityUrl,
2272
- getEntityScheduledActions: loadEntityScheduledActions
2273
- });
2274
- }
2275
-
2276
- if (status === 'loading') {
2277
- return createElement(AssetCard, {
2278
- size: size,
2279
- isLoading: true
2280
- });
2281
- }
2282
-
2283
- function renderDefaultCard(props) {
2284
- // isClickable has a default value, so omit it from the props
2285
- const builtinCardProps = { ...commonProps,
2286
- ...props,
2287
- getEntityScheduledActions: loadEntityScheduledActions,
2288
- asset: (props == null ? void 0 : props.entity) || commonProps.asset,
2289
- getAssetUrl: getEntityUrl
2290
- };
2291
- return createElement(WrappedAssetCard, { ...builtinCardProps
2292
- });
2293
- }
2294
-
2295
- if (props.renderCustomCard) {
2296
- const customProps = { ...commonProps,
2297
- entity: commonProps.asset
2298
- }; // LinkActionsProps are injected higher SingleReferenceEditor/MultipleReferenceEditor
2299
-
2300
- const renderedCustomCard = props.renderCustomCard(customProps, {}, renderDefaultCard); // Only `false` indicates to render the original card. E.g. `null` would result in no card.
2301
-
2302
- if (renderedCustomCard !== false) {
2303
- return renderedCustomCard;
2304
- }
2305
- }
2306
-
2307
- return renderDefaultCard(); // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: Evaluate the dependencies
2308
- }, [props, status, asset]);
2309
- }
2310
-
2311
- function SingleMediaEditor(props) {
2312
- return createElement(SingleReferenceEditor, { ...props,
2313
- entityType: "Asset"
2314
- }, ({
2315
- entityId,
2316
- isDisabled,
2317
- setValue
2318
- }) => createElement(FetchingWrappedAssetCard, { ...props,
2319
- viewType: "big_card",
2320
- assetId: entityId,
2321
- isDisabled: isDisabled,
2322
- onRemove: () => {
2323
- setValue(null);
2324
- }
2325
- }));
2326
- }
2327
- SingleMediaEditor.defaultProps = {
2328
- isInitiallyDisabled: true
2329
- };
2330
-
2331
- const styles$6 = {
2332
- gridContainer: /*#__PURE__*/css({
2333
- position: 'relative',
2334
- display: 'flex',
2335
- flexWrap: 'wrap'
2336
- })
2337
- };
2338
- function MultipleMediaEditor(props) {
2339
- return createElement(MultipleReferenceEditor, { ...props,
2340
- entityType: "Asset"
2341
- }, childrenProps => createElement(SortableLinkList, { ...childrenProps,
2342
- className: cx({
2343
- [styles$6.gridContainer]: childrenProps.viewType === 'card'
2344
- }),
2345
- axis: childrenProps.viewType === 'card' ? 'xy' : 'y',
2346
- useDragHandle: true
2347
- }, ({
2348
- items,
2349
- item,
2350
- index,
2351
- isDisabled,
2352
- DragHandle
2353
- }) => createElement(FetchingWrappedAssetCard, { ...childrenProps,
2354
- isDisabled: isDisabled,
2355
- key: `${item.sys.id}-${index}`,
2356
- assetId: item.sys.id,
2357
- onRemove: () => {
2358
- childrenProps.setValue(items.filter((_value, i) => i !== index));
2359
- },
2360
- renderDragHandle: DragHandle
2361
- })));
2362
- }
2363
- MultipleMediaEditor.defaultProps = {
2364
- isInitiallyDisabled: true
2365
- };
2366
-
2367
- const resolveAsset = () => Promise.resolve(); // we don't want to show scheduled actions for resources
2368
-
2369
-
2370
- const resolveScheduledActions = () => Promise.resolve([]);
2371
-
2372
- function ContentfulEntryCard({
2373
- info,
2374
- isDisabled,
2375
- renderDragHandle,
2376
- onRemove,
2377
- onMoveTop,
2378
- onMoveBottom,
2379
- getEntryRouteHref
2380
- }) {
2381
- const resourceSys = info.resource.sys;
2382
- const spaceId = resourceSys.space.sys.id;
2383
- const environmentId = resourceSys.environment.sys.id;
2384
- const entryId = resourceSys.id;
2385
- const resourceHref = getEntryRouteHref({
2386
- spaceId,
2387
- environmentId,
2388
- entryId
2389
- }); // TODO: move this into `sdk.navigator.openEntry()`, note that it's signature only include the entry id (not a space or environment)
2390
-
2391
- const openEntryDetail = () => {
2392
- window.open(resourceHref, '_blank', 'noopener,noreferrer');
2393
- };
2394
-
2395
- return createElement(WrappedEntryCard, {
2396
- entry: info.resource,
2397
- isDisabled: isDisabled,
2398
- hasCardEditActions: false,
2399
- contentType: info.contentType,
2400
- // we use the default locale from the space the entry belongs to
2401
- // as we assume this gives a more consistent behaviour.
2402
- // locales will inevitably differ from space to space, so it's likely
2403
- // that the current locale does not exist in the "remote" space
2404
- localeCode: info.defaultLocaleCode,
2405
- defaultLocaleCode: info.defaultLocaleCode,
2406
- size: "small",
2407
- getAsset: resolveAsset,
2408
- getEntityScheduledActions: resolveScheduledActions,
2409
- spaceName: info.space.name,
2410
- renderDragHandle: renderDragHandle,
2411
- isClickable: true,
2412
- onEdit: openEntryDetail,
2413
- hasCardRemoveActions: Boolean(onRemove),
2414
- onRemove: onRemove,
2415
- onMoveBottom: onMoveBottom,
2416
- onMoveTop: onMoveTop,
2417
- entryUrl: resourceHref
2418
- });
2419
- }
2420
-
2421
- const styles$7 = {
2422
- card: /*#__PURE__*/css({
2423
- position: 'relative'
2424
- })
2425
- };
2426
- function UnsupportedEntityCard(props) {
2427
- return React__default.createElement(Card, {
2428
- className: styles$7.card
2429
- }, React__default.createElement(SectionHeading, {
2430
- marginBottom: "none"
2431
- }, "Resource type ", props.entityType, " is currently not supported"));
2432
- }
2433
-
2434
- function ResourceCardSkeleton() {
2435
- return createElement(EntryCard, {
2436
- size: "small",
2437
- isLoading: true
2438
- });
2439
- }
2440
-
2441
- function ExistingResourceCard(props) {
2442
- const {
2443
- resourceLink,
2444
- inView,
2445
- index = 0
2446
- } = props;
2447
- const resourceOptions = {
2448
- priority: index * -1,
2449
- enabled: inView
2450
- };
2451
- const {
2452
- data,
2453
- error
2454
- } = useResource(resourceLink.sys.linkType, resourceLink.sys.urn, resourceOptions);
2455
-
2456
- if (!data && !error) {
2457
- return createElement(ResourceCardSkeleton, null);
2458
- }
2459
-
2460
- if (data) {
2461
- return createElement(ContentfulEntryCard, {
2462
- info: data,
2463
- ...props
2464
- });
2465
- }
2466
-
2467
- if (isUnsupportedError(error)) {
2468
- return createElement(UnsupportedEntityCard, {
2469
- entityType: resourceLink.sys.linkType
2470
- });
2471
- }
2472
-
2473
- return createElement(MissingEntityCard, {
2474
- entityType: "Entry",
2475
- isDisabled: props.isDisabled,
2476
- onRemove: props.onRemove
2477
- });
2478
- }
2479
-
2480
- function ResourceCardWrapper(props) {
2481
- if (!props.resourceLink) {
2482
- return null;
2483
- }
2484
-
2485
- return createElement(ExistingResourceCard, { ...props,
2486
- resourceLink: props.resourceLink,
2487
- getEntryRouteHref: props.getEntryRouteHref
2488
- });
2489
- }
2490
-
2491
- function ResourceCard(props) {
2492
- const {
2493
- ref,
2494
- inView
2495
- } = useInView({
2496
- triggerOnce: true,
2497
- rootMargin: '300px 0px 0px 300px'
2498
- }); // Forma does not offer us to pass refs, so we need an additional wrapper here
2499
-
2500
- return createElement("div", {
2501
- ref: ref
2502
- }, createElement(ResourceCardWrapper, { ...props,
2503
- inView: inView
2504
- }));
2505
- }
2506
-
2507
- const toLinkItem = (entry, apiUrl) => ({
2508
- sys: {
2509
- type: 'ResourceLink',
2510
- linkType: 'Contentful:Entry',
2511
- urn: entry.sys.urn ?? `crn:${apiUrl}:::content:spaces/${entry.sys.space.sys.id}/entries/${entry.sys.id}`
2512
- }
2513
- });
2514
-
2515
- const getUpdatedValue = (field, entries, apiUrl) => {
2516
- const multiple = field.type === 'Array';
2517
-
2518
- if (multiple) {
2519
- const linkItems = entries.map(entry => toLinkItem(entry, apiUrl));
2520
- const prevValue = field.getValue() || [];
2521
- return [...prevValue, ...linkItems];
2522
- } else {
2523
- return toLinkItem(entries[0], apiUrl);
2524
- }
2525
- };
2526
-
2527
- function useResourceLinkActions({
2528
- dialogs,
2529
- field,
2530
- onAfterLink,
2531
- apiUrl
2532
- }) {
2533
- const handleAfterLink = useCallback(entries => {
2534
- if (!onAfterLink) {
2535
- return;
2536
- }
2537
-
2538
- entries.forEach(onAfterLink);
2539
- }, [onAfterLink]);
2540
- const onLinkedExisting = useMemo(() => {
2541
- return entries => {
2542
- const updatedValue = getUpdatedValue(field, entries, apiUrl);
2543
- field.setValue(updatedValue);
2544
- handleAfterLink(entries);
2545
- };
2546
- }, [field, handleAfterLink, apiUrl]);
2547
- const multiple = field.type === 'Array';
2548
- const onLinkExisting = useMemo(() => {
2549
- const promptSelection = multiple ? async () => // @ts-expect-error wait for update of app-sdk version
2550
- await dialogs.selectMultipleResourceEntries({
2551
- // @ts-expect-error wait for update of app-sdk version
2552
- allowedResources: field.allowedResources
2553
- }) : async () => [// @ts-expect-error wait for update of app-sdk version
2554
- await dialogs.selectSingleResourceEntry({
2555
- // @ts-expect-error wait for update of app-sdk version
2556
- allowedResources: field.allowedResources
2557
- })];
2558
- return async () => {
2559
- const res = await promptSelection();
2560
-
2561
- if (!res) {
2562
- return;
2563
- }
2564
-
2565
- onLinkedExisting(res);
2566
- }; // @ts-expect-error wait for update of app-sdk version
2567
- }, [dialogs, field.allowedResources, multiple, onLinkedExisting]);
2568
- return {
2569
- onLinkExisting,
2570
- onLinkedExisting,
2571
- // hardcoded values to match interface for standard reference field actions
2572
- entityType: 'Entry',
2573
- contentTypes: [],
2574
- canCreateEntity: false,
2575
- canLinkMultiple: multiple,
2576
- canLinkEntity: true,
2577
- isDisabled: false,
2578
- isEmpty: false,
2579
- isFull: false,
2580
- // eslint-disable-next-line -- hardcoded values to match interface for standard reference field actions
2581
- onCreate: async () => {},
2582
- // eslint-disable-next-line -- hardcoded values to match interface for standard reference field actions
2583
- onCreated: () => {}
2584
- };
2585
- }
2586
-
2587
- function ResourceEditor(props) {
2588
- const {
2589
- setValue,
2590
- items,
2591
- apiUrl
2592
- } = props;
2593
- const onSortStart = useCallback((_, event) => event.preventDefault(), []);
2594
- const onSortEnd = useCallback(({
2595
- oldIndex,
2596
- newIndex
2597
- }) => {
2598
- const newItems = arrayMove(items, oldIndex, newIndex);
2599
- setValue(newItems);
2600
- }, [items, setValue]);
2601
- const onMove = useCallback((oldIndex, newIndex) => {
2602
- const newItems = arrayMove(items, oldIndex, newIndex);
2603
- setValue(newItems);
2604
- }, [items, setValue]);
2605
- const onRemoteItemAtIndex = useCallback(index => {
2606
- setValue(items.filter((_v, i) => i !== index));
2607
- }, [items, setValue]);
2608
- const {
2609
- dialogs,
2610
- field
2611
- } = props.sdk;
2612
- const linkActionsProps = useResourceLinkActions({
2613
- dialogs,
2614
- field,
2615
- apiUrl
2616
- });
2617
- return createElement(Fragment, null, props.children({ ...props,
2618
- onSortStart,
2619
- onSortEnd,
2620
- onMove,
2621
- onRemoteItemAtIndex
2622
- }), createElement(CombinedLinkEntityActions, { ...linkActionsProps,
2623
- renderCustomActions: props.renderCustomActions
2624
- }));
2625
- } // provides memoized callbacks bound to a given item
2626
-
2627
-
2628
- function WithPerItemCallbacks({
2629
- listLength,
2630
- index,
2631
- onMove,
2632
- onRemoteItemAtIndex,
2633
- children
2634
- }) {
2635
- const handleMoveTop = useMemo(() => index > 0 ? () => onMove(index, 0) : undefined, [index, onMove]);
2636
- const handleMoveBottom = useMemo(() => index < listLength - 1 ? () => onMove(index, listLength - 1) : undefined, [index, onMove, listLength]);
2637
- const handleRemove = useCallback(() => onRemoteItemAtIndex(index), [index, onRemoteItemAtIndex]);
2638
- return createElement(Fragment, null, children({
2639
- onMoveBottom: handleMoveBottom,
2640
- onMoveTop: handleMoveTop,
2641
- onRemove: handleRemove
2642
- }));
2643
- }
2644
-
2645
- const EMPTY_ARRAY = [];
2646
- function MultipleResourceReferenceEditor(props) {
2647
- return createElement(EntityProvider, {
2648
- sdk: props.sdk
2649
- }, createElement(FieldConnector, {
2650
- throttle: 0,
2651
- field: props.sdk.field,
2652
- isInitiallyDisabled: props.isInitiallyDisabled,
2653
- isEqualValues: deepEqual
2654
- }, ({
2655
- value,
2656
- disabled,
2657
- setValue,
2658
- externalReset
2659
- }) => {
2660
- return createElement(ResourceEditor, { ...props,
2661
- items: value || EMPTY_ARRAY,
2662
- isDisabled: disabled,
2663
- setValue: setValue,
2664
- renderCustomActions: props.renderCustomActions,
2665
- key: `${externalReset}-list`
2666
- }, editorProps => createElement(SortableLinkList, { ...editorProps
2667
- }, ({
2668
- item,
2669
- isDisabled,
2670
- DragHandle,
2671
- index
2672
- }) => createElement(WithPerItemCallbacks, {
2673
- index: index,
2674
- onMove: editorProps.onMove,
2675
- onRemoteItemAtIndex: editorProps.onRemoteItemAtIndex,
2676
- listLength: (value == null ? void 0 : value.length) || 0
2677
- }, ({
2678
- onMoveBottom,
2679
- onMoveTop,
2680
- onRemove
2681
- }) => createElement(ResourceCard, {
2682
- index: index,
2683
- resourceLink: item,
2684
- isDisabled: isDisabled,
2685
- renderDragHandle: DragHandle,
2686
- onMoveTop: onMoveTop,
2687
- onMoveBottom: onMoveBottom,
2688
- onRemove: onRemove,
2689
- getEntryRouteHref: props.getEntryRouteHref
2690
- }))));
2691
- }));
2692
- }
2693
-
2694
- function SingleResourceReferenceEditor(props) {
2695
- const {
2696
- dialogs,
2697
- field
2698
- } = props.sdk;
2699
- const linkActionsProps = useResourceLinkActions({
2700
- dialogs,
2701
- field,
2702
- apiUrl: props.apiUrl
2703
- });
2704
- return createElement(EntityProvider, {
2705
- sdk: props.sdk
2706
- }, createElement(FieldConnector, {
2707
- throttle: 0,
2708
- field: props.sdk.field,
2709
- isInitiallyDisabled: props.isInitiallyDisabled,
2710
- isEqualValues: deepEqual
2711
- }, ({
2712
- value,
2713
- disabled
2714
- }) => {
2715
- return value ? createElement(ResourceCard, {
2716
- onRemove: () => props.sdk.field.removeValue(),
2717
- resourceLink: value,
2718
- isDisabled: disabled,
2719
- getEntryRouteHref: props.getEntryRouteHref
2720
- }) : createElement(CombinedLinkEntityActions, { ...linkActionsProps,
2721
- renderCustomActions: props.renderCustomActions
2722
- });
2723
- }));
2724
- }
2725
-
2726
- export { AssetThumbnail, CombinedLinkActions, CreateEntryLinkButton, CreateEntryMenuTrigger, EntityProvider, MissingEntityCard, MultipleEntryReferenceEditor, MultipleMediaEditor, MultipleResourceReferenceEditor, ScheduledIconWithTooltip, SingleEntryReferenceEditor, SingleMediaEditor, SingleResourceReferenceEditor, SortableLinkList, WrappedAssetCard, WrappedEntryCard, getScheduleTooltipContent, useEntity, useEntityLoader, useResource };
2727
- //# sourceMappingURL=field-editor-reference.esm.js.map