@strapi/content-manager 5.60.0 → 5.61.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 (256) hide show
  1. package/dist/admin/constants/customOrder.js +14 -0
  2. package/dist/admin/constants/customOrder.js.map +1 -0
  3. package/dist/admin/constants/customOrder.mjs +10 -0
  4. package/dist/admin/constants/customOrder.mjs.map +1 -0
  5. package/dist/admin/hooks/useDocument.js +22 -11
  6. package/dist/admin/hooks/useDocument.js.map +1 -1
  7. package/dist/admin/hooks/useDocument.mjs +22 -11
  8. package/dist/admin/hooks/useDocument.mjs.map +1 -1
  9. package/dist/admin/hooks/useDocumentLayout.js.map +1 -1
  10. package/dist/admin/hooks/useDocumentLayout.mjs.map +1 -1
  11. package/dist/admin/hooks/useHasBeenNearViewport.js +70 -0
  12. package/dist/admin/hooks/useHasBeenNearViewport.js.map +1 -0
  13. package/dist/admin/hooks/useHasBeenNearViewport.mjs +46 -0
  14. package/dist/admin/hooks/useHasBeenNearViewport.mjs.map +1 -0
  15. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Image.js +102 -32
  16. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Image.js.map +1 -1
  17. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Image.mjs +104 -35
  18. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Image.mjs.map +1 -1
  19. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.js +20 -17
  20. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.js.map +1 -1
  21. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.mjs +20 -17
  22. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.mjs.map +1 -1
  23. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.js +15 -1
  24. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.js.map +1 -1
  25. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.mjs +17 -3
  26. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.mjs.map +1 -1
  27. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/plugins/withStrapiSchema.js +21 -0
  28. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/plugins/withStrapiSchema.js.map +1 -1
  29. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/plugins/withStrapiSchema.mjs +22 -1
  30. package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/plugins/withStrapiSchema.mjs.map +1 -1
  31. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js +4 -7
  32. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js.map +1 -1
  33. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs +4 -7
  34. package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs.map +1 -1
  35. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +46 -10
  36. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
  37. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +47 -11
  38. package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
  39. package/dist/admin/pages/EditView/components/FormLayout.js +201 -51
  40. package/dist/admin/pages/EditView/components/FormLayout.js.map +1 -1
  41. package/dist/admin/pages/EditView/components/FormLayout.mjs +202 -53
  42. package/dist/admin/pages/EditView/components/FormLayout.mjs.map +1 -1
  43. package/dist/admin/pages/ListConfiguration/components/Settings.js +31 -2
  44. package/dist/admin/pages/ListConfiguration/components/Settings.js.map +1 -1
  45. package/dist/admin/pages/ListConfiguration/components/Settings.mjs +31 -2
  46. package/dist/admin/pages/ListConfiguration/components/Settings.mjs.map +1 -1
  47. package/dist/admin/pages/ListView/ListViewPage.js +196 -132
  48. package/dist/admin/pages/ListView/ListViewPage.js.map +1 -1
  49. package/dist/admin/pages/ListView/ListViewPage.mjs +196 -132
  50. package/dist/admin/pages/ListView/ListViewPage.mjs.map +1 -1
  51. package/dist/admin/pages/ListView/components/CustomOrder.js +571 -0
  52. package/dist/admin/pages/ListView/components/CustomOrder.js.map +1 -0
  53. package/dist/admin/pages/ListView/components/CustomOrder.mjs +546 -0
  54. package/dist/admin/pages/ListView/components/CustomOrder.mjs.map +1 -0
  55. package/dist/admin/pages/ListView/components/TableActions.js +4 -1
  56. package/dist/admin/pages/ListView/components/TableActions.js.map +1 -1
  57. package/dist/admin/pages/ListView/components/TableActions.mjs +4 -1
  58. package/dist/admin/pages/ListView/components/TableActions.mjs.map +1 -1
  59. package/dist/admin/services/documents.js +22 -1
  60. package/dist/admin/services/documents.js.map +1 -1
  61. package/dist/admin/services/documents.mjs +21 -2
  62. package/dist/admin/services/documents.mjs.map +1 -1
  63. package/dist/admin/src/constants/customOrder.d.ts +8 -0
  64. package/dist/admin/src/exports.d.ts +1 -1
  65. package/dist/admin/src/hooks/useDocument.d.ts +4 -0
  66. package/dist/admin/src/hooks/useDocumentLayout.d.ts +6 -2
  67. package/dist/admin/src/hooks/useHasBeenNearViewport.d.ts +13 -0
  68. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Image.d.ts +13 -1
  69. package/dist/admin/src/pages/EditView/components/FormLayout.d.ts +11 -1
  70. package/dist/admin/src/pages/ListView/components/CustomOrder.d.ts +72 -0
  71. package/dist/admin/src/services/documents.d.ts +10 -2
  72. package/dist/admin/translations/en.json.js +9 -0
  73. package/dist/admin/translations/en.json.js.map +1 -1
  74. package/dist/admin/translations/en.json.mjs +9 -0
  75. package/dist/admin/translations/en.json.mjs.map +1 -1
  76. package/dist/server/audit-logs.js +36 -0
  77. package/dist/server/audit-logs.js.map +1 -0
  78. package/dist/server/audit-logs.mjs +33 -0
  79. package/dist/server/audit-logs.mjs.map +1 -0
  80. package/dist/server/bootstrap.js +8 -0
  81. package/dist/server/bootstrap.js.map +1 -1
  82. package/dist/server/bootstrap.mjs +8 -0
  83. package/dist/server/bootstrap.mjs.map +1 -1
  84. package/dist/server/controllers/collection-types.js +8 -0
  85. package/dist/server/controllers/collection-types.js.map +1 -1
  86. package/dist/server/controllers/collection-types.mjs +8 -0
  87. package/dist/server/controllers/collection-types.mjs.map +1 -1
  88. package/dist/server/controllers/content-types.js +15 -0
  89. package/dist/server/controllers/content-types.js.map +1 -1
  90. package/dist/server/controllers/content-types.mjs +16 -1
  91. package/dist/server/controllers/content-types.mjs.map +1 -1
  92. package/dist/server/controllers/index.js +3 -1
  93. package/dist/server/controllers/index.js.map +1 -1
  94. package/dist/server/controllers/index.mjs +2 -0
  95. package/dist/server/controllers/index.mjs.map +1 -1
  96. package/dist/server/controllers/relations.js +5 -1
  97. package/dist/server/controllers/relations.js.map +1 -1
  98. package/dist/server/controllers/relations.mjs +5 -1
  99. package/dist/server/controllers/relations.mjs.map +1 -1
  100. package/dist/server/controllers/single-types.js +10 -1
  101. package/dist/server/controllers/single-types.js.map +1 -1
  102. package/dist/server/controllers/single-types.mjs +10 -1
  103. package/dist/server/controllers/single-types.mjs.map +1 -1
  104. package/dist/server/controllers/validation/model-configuration.js +2 -1
  105. package/dist/server/controllers/validation/model-configuration.js.map +1 -1
  106. package/dist/server/controllers/validation/model-configuration.mjs +2 -1
  107. package/dist/server/controllers/validation/model-configuration.mjs.map +1 -1
  108. package/dist/server/custom-order/constants.js +21 -0
  109. package/dist/server/custom-order/constants.js.map +1 -0
  110. package/dist/server/custom-order/constants.mjs +17 -0
  111. package/dist/server/custom-order/constants.mjs.map +1 -0
  112. package/dist/server/custom-order/controllers/custom-order.js +77 -0
  113. package/dist/server/custom-order/controllers/custom-order.js.map +1 -0
  114. package/dist/server/custom-order/controllers/custom-order.mjs +75 -0
  115. package/dist/server/custom-order/controllers/custom-order.mjs.map +1 -0
  116. package/dist/server/custom-order/controllers/index.js +10 -0
  117. package/dist/server/custom-order/controllers/index.js.map +1 -0
  118. package/dist/server/custom-order/controllers/index.mjs +8 -0
  119. package/dist/server/custom-order/controllers/index.mjs.map +1 -0
  120. package/dist/server/custom-order/index.js +71 -0
  121. package/dist/server/custom-order/index.js.map +1 -0
  122. package/dist/server/custom-order/index.mjs +69 -0
  123. package/dist/server/custom-order/index.mjs.map +1 -0
  124. package/dist/server/custom-order/routes/custom-order.js +38 -0
  125. package/dist/server/custom-order/routes/custom-order.js.map +1 -0
  126. package/dist/server/custom-order/routes/custom-order.mjs +36 -0
  127. package/dist/server/custom-order/routes/custom-order.mjs.map +1 -0
  128. package/dist/server/custom-order/routes/index.js +13 -0
  129. package/dist/server/custom-order/routes/index.js.map +1 -0
  130. package/dist/server/custom-order/routes/index.mjs +11 -0
  131. package/dist/server/custom-order/routes/index.mjs.map +1 -0
  132. package/dist/server/custom-order/services/custom-order.js +286 -0
  133. package/dist/server/custom-order/services/custom-order.js.map +1 -0
  134. package/dist/server/custom-order/services/custom-order.mjs +284 -0
  135. package/dist/server/custom-order/services/custom-order.mjs.map +1 -0
  136. package/dist/server/custom-order/services/document-middleware.js +83 -0
  137. package/dist/server/custom-order/services/document-middleware.js.map +1 -0
  138. package/dist/server/custom-order/services/document-middleware.mjs +81 -0
  139. package/dist/server/custom-order/services/document-middleware.mjs.map +1 -0
  140. package/dist/server/custom-order/services/index.js +10 -0
  141. package/dist/server/custom-order/services/index.js.map +1 -0
  142. package/dist/server/custom-order/services/index.mjs +8 -0
  143. package/dist/server/custom-order/services/index.mjs.map +1 -0
  144. package/dist/server/custom-order/services/utils.js +152 -0
  145. package/dist/server/custom-order/services/utils.js.map +1 -0
  146. package/dist/server/custom-order/services/utils.mjs +148 -0
  147. package/dist/server/custom-order/services/utils.mjs.map +1 -0
  148. package/dist/server/custom-order/utils.js +13 -0
  149. package/dist/server/custom-order/utils.js.map +1 -0
  150. package/dist/server/custom-order/utils.mjs +10 -0
  151. package/dist/server/custom-order/utils.mjs.map +1 -0
  152. package/dist/server/history/constants.js +2 -1
  153. package/dist/server/history/constants.js.map +1 -1
  154. package/dist/server/history/constants.mjs +2 -1
  155. package/dist/server/history/constants.mjs.map +1 -1
  156. package/dist/server/history/controllers/history-version.js +6 -2
  157. package/dist/server/history/controllers/history-version.js.map +1 -1
  158. package/dist/server/history/controllers/history-version.mjs +6 -2
  159. package/dist/server/history/controllers/history-version.mjs.map +1 -1
  160. package/dist/server/history/services/history.js +23 -6
  161. package/dist/server/history/services/history.js.map +1 -1
  162. package/dist/server/history/services/history.mjs +24 -7
  163. package/dist/server/history/services/history.mjs.map +1 -1
  164. package/dist/server/history/services/lifecycles.js +2 -16
  165. package/dist/server/history/services/lifecycles.js.map +1 -1
  166. package/dist/server/history/services/lifecycles.mjs +2 -16
  167. package/dist/server/history/services/lifecycles.mjs.map +1 -1
  168. package/dist/server/register.js +4 -0
  169. package/dist/server/register.js.map +1 -1
  170. package/dist/server/register.mjs +4 -0
  171. package/dist/server/register.mjs.map +1 -1
  172. package/dist/server/routes/index.js +3 -1
  173. package/dist/server/routes/index.js.map +1 -1
  174. package/dist/server/routes/index.mjs +2 -0
  175. package/dist/server/routes/index.mjs.map +1 -1
  176. package/dist/server/services/content-types.js +2 -2
  177. package/dist/server/services/content-types.js.map +1 -1
  178. package/dist/server/services/content-types.mjs +2 -2
  179. package/dist/server/services/content-types.mjs.map +1 -1
  180. package/dist/server/services/document-manager.js +13 -0
  181. package/dist/server/services/document-manager.js.map +1 -1
  182. package/dist/server/services/document-manager.mjs +14 -1
  183. package/dist/server/services/document-manager.mjs.map +1 -1
  184. package/dist/server/services/index.js +3 -1
  185. package/dist/server/services/index.js.map +1 -1
  186. package/dist/server/services/index.mjs +2 -0
  187. package/dist/server/services/index.mjs.map +1 -1
  188. package/dist/server/services/utils/configuration/settings.js +2 -1
  189. package/dist/server/services/utils/configuration/settings.js.map +1 -1
  190. package/dist/server/services/utils/configuration/settings.mjs +2 -1
  191. package/dist/server/services/utils/configuration/settings.mjs.map +1 -1
  192. package/dist/server/src/audit-logs.d.ts +40 -0
  193. package/dist/server/src/audit-logs.d.ts.map +1 -0
  194. package/dist/server/src/bootstrap.d.ts.map +1 -1
  195. package/dist/server/src/controllers/collection-types.d.ts +1 -1
  196. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  197. package/dist/server/src/controllers/content-types.d.ts.map +1 -1
  198. package/dist/server/src/controllers/index.d.ts +21 -3
  199. package/dist/server/src/controllers/index.d.ts.map +1 -1
  200. package/dist/server/src/controllers/relations.d.ts +19 -1
  201. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  202. package/dist/server/src/controllers/single-types.d.ts +1 -1
  203. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  204. package/dist/server/src/custom-order/constants.d.ts +18 -0
  205. package/dist/server/src/custom-order/constants.d.ts.map +1 -0
  206. package/dist/server/src/custom-order/controllers/custom-order.d.ts +8 -0
  207. package/dist/server/src/custom-order/controllers/custom-order.d.ts.map +1 -0
  208. package/dist/server/src/custom-order/controllers/index.d.ts +3 -0
  209. package/dist/server/src/custom-order/controllers/index.d.ts.map +1 -0
  210. package/dist/server/src/custom-order/index.d.ts +8 -0
  211. package/dist/server/src/custom-order/index.d.ts.map +1 -0
  212. package/dist/server/src/custom-order/routes/custom-order.d.ts +4 -0
  213. package/dist/server/src/custom-order/routes/custom-order.d.ts.map +1 -0
  214. package/dist/server/src/custom-order/routes/index.d.ts +10 -0
  215. package/dist/server/src/custom-order/routes/index.d.ts.map +1 -0
  216. package/dist/server/src/custom-order/services/custom-order.d.ts +43 -0
  217. package/dist/server/src/custom-order/services/custom-order.d.ts.map +1 -0
  218. package/dist/server/src/custom-order/services/document-middleware.d.ts +16 -0
  219. package/dist/server/src/custom-order/services/document-middleware.d.ts.map +1 -0
  220. package/dist/server/src/custom-order/services/index.d.ts +24 -0
  221. package/dist/server/src/custom-order/services/index.d.ts.map +1 -0
  222. package/dist/server/src/custom-order/services/utils.d.ts +34 -0
  223. package/dist/server/src/custom-order/services/utils.d.ts.map +1 -0
  224. package/dist/server/src/custom-order/utils.d.ts +6 -0
  225. package/dist/server/src/custom-order/utils.d.ts.map +1 -0
  226. package/dist/server/src/history/constants.d.ts.map +1 -1
  227. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  228. package/dist/server/src/history/services/history.d.ts.map +1 -1
  229. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  230. package/dist/server/src/index.d.ts +22 -6
  231. package/dist/server/src/index.d.ts.map +1 -1
  232. package/dist/server/src/register.d.ts.map +1 -1
  233. package/dist/server/src/routes/index.d.ts.map +1 -1
  234. package/dist/server/src/services/content-types.d.ts +1 -3
  235. package/dist/server/src/services/content-types.d.ts.map +1 -1
  236. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  237. package/dist/server/src/services/index.d.ts +1 -3
  238. package/dist/server/src/services/index.d.ts.map +1 -1
  239. package/dist/server/src/services/utils/configuration/settings.d.ts.map +1 -1
  240. package/dist/shared/contracts/content-types.d.ts +5 -0
  241. package/dist/shared/contracts/content-types.d.ts.map +1 -1
  242. package/dist/shared/contracts/custom-order.d.ts +33 -0
  243. package/dist/shared/contracts/custom-order.d.ts.map +1 -0
  244. package/dist/shared/contracts/custom-order.js +3 -0
  245. package/dist/shared/contracts/custom-order.js.map +1 -0
  246. package/dist/shared/contracts/custom-order.mjs +2 -0
  247. package/dist/shared/contracts/custom-order.mjs.map +1 -0
  248. package/dist/shared/contracts/history-versions.d.ts +0 -1
  249. package/dist/shared/contracts/history-versions.d.ts.map +1 -1
  250. package/dist/shared/contracts/index.d.ts +1 -0
  251. package/dist/shared/contracts/index.d.ts.map +1 -1
  252. package/dist/shared/contracts/index.js +2 -0
  253. package/dist/shared/contracts/index.js.map +1 -1
  254. package/dist/shared/contracts/index.mjs +2 -0
  255. package/dist/shared/contracts/index.mjs.map +1 -1
  256. package/package.json +8 -8
@@ -2,9 +2,11 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
5
6
  var designSystem = require('@strapi/design-system');
6
7
  var reactIntl = require('react-intl');
7
8
  var styledComponents = require('styled-components');
9
+ var useHasBeenNearViewport = require('../../../hooks/useHasBeenNearViewport.js');
8
10
  var InputRenderer = require('./InputRenderer.js');
9
11
 
10
12
  function _interopNamespace(e) {
@@ -27,6 +29,139 @@ function _interopNamespace(e) {
27
29
 
28
30
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
31
 
32
+ /**
33
+ * Rough rendered heights, only used to size placeholders so the scrollbar stays sensible.
34
+ */ const ESTIMATED_HEIGHT = {
35
+ richtext: 320,
36
+ blocks: 320,
37
+ json: 240,
38
+ component: 240,
39
+ dynamiczone: 160,
40
+ relation: 120,
41
+ media: 160,
42
+ text: 120
43
+ };
44
+ const DEFAULT_ESTIMATED_HEIGHT = 80;
45
+ const requestIdle = (callback)=>typeof window.requestIdleCallback === 'function' ? window.requestIdleCallback(callback, {
46
+ timeout: 2000
47
+ }) : window.setTimeout(callback, 16);
48
+ const cancelIdle = (handle)=>typeof window.cancelIdleCallback === 'function' ? window.cancelIdleCallback(handle) : window.clearTimeout(handle);
49
+ /**
50
+ * Mounts the rows that weren't needed for the first paint, one per idle period, in the order
51
+ * they were scheduled (top to bottom). Rows are then already rendered when the user scrolls to
52
+ * them, and the browser search finds every field.
53
+ */ const createIdleMountQueue = ()=>{
54
+ let queue = [];
55
+ let handle = null;
56
+ const flush = ()=>{
57
+ handle = null;
58
+ queue.shift()?.();
59
+ if (queue.length > 0) {
60
+ handle = requestIdle(flush);
61
+ }
62
+ };
63
+ return {
64
+ schedule: (mount)=>{
65
+ queue.push(mount);
66
+ if (handle === null) {
67
+ handle = requestIdle(flush);
68
+ }
69
+ return ()=>{
70
+ queue = queue.filter((scheduled)=>scheduled !== mount);
71
+ };
72
+ },
73
+ dispose: ()=>{
74
+ queue = [];
75
+ if (handle !== null) {
76
+ cancelIdle(handle);
77
+ handle = null;
78
+ }
79
+ }
80
+ };
81
+ };
82
+ const IdleMountContext = /*#__PURE__*/ React__namespace.createContext(null);
83
+ const useIdleMountQueue = ()=>{
84
+ const [queue] = React__namespace.useState(createIdleMountQueue);
85
+ React__namespace.useEffect(()=>queue.dispose, [
86
+ queue
87
+ ]);
88
+ return queue.schedule;
89
+ };
90
+ /**
91
+ * Some inputs set a value when they mount: a required UID fetches its default value, and custom
92
+ * fields may set a default. Their rows are mounted with the page, as they always were, so that
93
+ * saving right after the page loads doesn't miss those values. Non-repeatable components are
94
+ * expanded by default, so the inputs they contain count too.
95
+ */ const hasMountSideEffects = (attribute, components, depth = 0)=>{
96
+ if (attribute.type === 'uid' && attribute.required || 'customField' in attribute) {
97
+ return true;
98
+ }
99
+ if (attribute.type === 'component' && !attribute.repeatable && depth < 10) {
100
+ return Object.values(components[attribute.component]?.attributes ?? {}).some((nested)=>hasMountSideEffects(nested, components, depth + 1));
101
+ }
102
+ return false;
103
+ };
104
+ /**
105
+ * Mounts its children once they get close to the viewport, or in the background when the
106
+ * browser is idle, then keeps them mounted. Rendering every field of a large content type
107
+ * upfront is what makes the edit view slow: the cost of the first render should depend on
108
+ * the screen, not on the size of the model.
109
+ */ const LazyFormRow = ({ fields, mountImmediately = false, children })=>{
110
+ const scheduleMount = React__namespace.useContext(IdleMountContext);
111
+ const placeholderRef = React__namespace.useRef(null);
112
+ const [isVisible, setIsVisible] = React__namespace.useState(()=>mountImmediately || typeof IntersectionObserver === 'undefined');
113
+ // A field in error has to be mounted so the error is displayed and can receive the focus.
114
+ const hasError = strapiAdmin.useForm('LazyFormRow', (state)=>fields.some(({ name })=>name in state.errors));
115
+ // Once mounted for an error, the row stays mounted when the errors are cleared (every
116
+ // validation starts by clearing them), so its state (e.g. open blocks) is preserved.
117
+ React__namespace.useEffect(()=>{
118
+ if (hasError) {
119
+ setIsVisible(true);
120
+ }
121
+ }, [
122
+ hasError
123
+ ]);
124
+ const shouldMount = isVisible || hasError;
125
+ React__namespace.useEffect(()=>{
126
+ const placeholder = placeholderRef.current;
127
+ if (shouldMount || !placeholder) {
128
+ return;
129
+ }
130
+ const observer = new IntersectionObserver((entries)=>{
131
+ if (entries.some((entry)=>entry.isIntersecting)) {
132
+ setIsVisible(true);
133
+ }
134
+ }, {
135
+ root: useHasBeenNearViewport.getScrollParent(placeholder),
136
+ rootMargin: useHasBeenNearViewport.NEAR_VIEWPORT_MARGIN
137
+ });
138
+ observer.observe(placeholder);
139
+ // Low priority: user input interrupts the render of a row mounted in the background.
140
+ const cancelIdleMount = scheduleMount?.(()=>React__namespace.startTransition(()=>{
141
+ setIsVisible(true);
142
+ }));
143
+ return ()=>{
144
+ observer.disconnect();
145
+ cancelIdleMount?.();
146
+ };
147
+ }, [
148
+ shouldMount,
149
+ scheduleMount
150
+ ]);
151
+ if (shouldMount) {
152
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
153
+ children: children
154
+ });
155
+ }
156
+ const height = Math.max(...fields.map(({ type })=>ESTIMATED_HEIGHT[type] ?? DEFAULT_ESTIMATED_HEIGHT));
157
+ return /*#__PURE__*/ jsxRuntime.jsx("div", {
158
+ ref: placeholderRef,
159
+ style: {
160
+ height
161
+ },
162
+ "aria-hidden": true
163
+ });
164
+ };
30
165
  const ResponsiveGridRoot = styledComponents.styled(designSystem.Grid.Root)`
31
166
  container-type: inline-size;
32
167
  `;
@@ -55,6 +190,7 @@ const panelStyles = {
55
190
  };
56
191
  const FormLayout = /*#__PURE__*/ React__namespace.memo(({ layout, document, hasBackground = true })=>{
57
192
  const { formatMessage } = reactIntl.useIntl();
193
+ const scheduleMount = useIdleMountQueue();
58
194
  const modelUid = document.schema?.uid;
59
195
  const getLabel = (name, label)=>{
60
196
  return formatMessage({
@@ -62,61 +198,74 @@ const FormLayout = /*#__PURE__*/ React__namespace.memo(({ layout, document, hasB
62
198
  defaultMessage: label
63
199
  });
64
200
  };
65
- return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
66
- direction: "column",
67
- alignItems: "stretch",
68
- gap: 6,
69
- children: layout.map((panel)=>{
70
- if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
71
- const [row] = panel;
72
- const [field] = row;
73
- return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
74
- gap: 4,
75
- children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
76
- col: 12,
77
- s: 12,
78
- xs: 12,
201
+ return /*#__PURE__*/ jsxRuntime.jsx(IdleMountContext.Provider, {
202
+ value: scheduleMount,
203
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
204
+ direction: "column",
205
+ alignItems: "stretch",
206
+ gap: 6,
207
+ children: layout.map((panel)=>{
208
+ if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
209
+ const [row] = panel;
210
+ const [field] = row;
211
+ return /*#__PURE__*/ jsxRuntime.jsx(LazyFormRow, {
212
+ fields: [
213
+ field
214
+ ],
215
+ mountImmediately: hasMountSideEffects(field.attribute, document.components),
216
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
217
+ gap: 4,
218
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
219
+ col: 12,
220
+ s: 12,
221
+ xs: 12,
222
+ direction: "column",
223
+ alignItems: "stretch",
224
+ children: /*#__PURE__*/ jsxRuntime.jsx(InputRenderer.InputRenderer, {
225
+ ...field,
226
+ label: getLabel(field.name, field.label),
227
+ document: document
228
+ })
229
+ })
230
+ })
231
+ }, field.name);
232
+ }
233
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
234
+ ...hasBackground && panelStyles,
235
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
79
236
  direction: "column",
80
237
  alignItems: "stretch",
81
- children: /*#__PURE__*/ jsxRuntime.jsx(InputRenderer.InputRenderer, {
82
- ...field,
83
- label: getLabel(field.name, field.label),
84
- document: document
85
- })
86
- })
87
- }, field.name);
88
- }
89
- return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
90
- ...hasBackground && panelStyles,
91
- children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
92
- direction: "column",
93
- alignItems: "stretch",
94
- gap: 6,
95
- children: panel.map((row)=>{
96
- return /*#__PURE__*/ jsxRuntime.jsx(ResponsiveGridRoot, {
97
- gap: {
98
- initial: 6,
99
- medium: 4
100
- },
101
- children: row.map(({ size, ...field })=>{
102
- return /*#__PURE__*/ jsxRuntime.jsx(ResponsiveGridItem, {
103
- col: size,
104
- s: 12,
105
- xs: 12,
106
- direction: "column",
107
- alignItems: "stretch",
108
- children: /*#__PURE__*/ jsxRuntime.jsx(InputRenderer.InputRenderer, {
109
- ...field,
110
- size: size,
111
- label: getLabel(field.name, field.label),
112
- document: document
238
+ gap: 6,
239
+ children: panel.map((row)=>{
240
+ return /*#__PURE__*/ jsxRuntime.jsx(LazyFormRow, {
241
+ fields: row,
242
+ mountImmediately: row.some((field)=>hasMountSideEffects(field.attribute, document.components)),
243
+ children: /*#__PURE__*/ jsxRuntime.jsx(ResponsiveGridRoot, {
244
+ gap: {
245
+ initial: 6,
246
+ medium: 4
247
+ },
248
+ children: row.map(({ size, ...field })=>{
249
+ return /*#__PURE__*/ jsxRuntime.jsx(ResponsiveGridItem, {
250
+ col: size,
251
+ s: 12,
252
+ xs: 12,
253
+ direction: "column",
254
+ alignItems: "stretch",
255
+ children: /*#__PURE__*/ jsxRuntime.jsx(InputRenderer.InputRenderer, {
256
+ ...field,
257
+ size: size,
258
+ label: getLabel(field.name, field.label),
259
+ document: document
260
+ })
261
+ }, field.name);
113
262
  })
114
- }, field.name);
115
- })
116
- }, row.map((field)=>field.name).join('.'));
263
+ })
264
+ }, row.map((field)=>field.name).join('.'));
265
+ })
117
266
  })
118
- })
119
- }, panel.flatMap((row)=>row.map((field)=>field.name)).join('.'));
267
+ }, panel.flatMap((row)=>row.map((field)=>field.name)).join('.'));
268
+ })
120
269
  })
121
270
  });
122
271
  });
@@ -125,4 +274,5 @@ FormLayout.displayName = 'FormLayout';
125
274
  exports.FormLayout = FormLayout;
126
275
  exports.ResponsiveGridItem = ResponsiveGridItem;
127
276
  exports.ResponsiveGridRoot = ResponsiveGridRoot;
277
+ exports.hasMountSideEffects = hasMountSideEffects;
128
278
  //# sourceMappingURL=FormLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormLayout.js","sources":["../../../../../admin/src/pages/EditView/components/FormLayout.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Box, BoxProps, Flex, Grid } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { EditLayout } from '../../../hooks/useDocumentLayout';\n\nimport { InputRenderer } from './InputRenderer';\n\nimport type { UseDocument } from '../../../hooks/useDocument';\n\nexport const ResponsiveGridRoot = styled(Grid.Root)`\n container-type: inline-size;\n`;\n\nexport const ResponsiveGridItem =\n /**\n * TODO:\n * JSDOM cannot handle container queries.\n * This is a temporary workaround so that tests do not fail in the CI when jestdom throws an error\n * for failing to parse the stylesheet.\n */\n process.env.NODE_ENV !== 'test'\n ? styled(Grid.Item)<{ col: number }>`\n grid-column: span 12;\n ${({ theme }) => theme.breakpoints.medium} {\n ${({ col }) => col && `grid-column: span ${col};`}\n }\n `\n : styled(Grid.Item)<{ col: number }>`\n grid-column: span 12;\n `;\n\nconst panelStyles = {\n padding: {\n initial: 4,\n medium: 6,\n },\n borderColor: 'neutral150',\n background: 'neutral0',\n hasRadius: true,\n shadow: 'tableShadow',\n} satisfies BoxProps;\n\ninterface FormLayoutProps extends Pick<EditLayout, 'layout'> {\n hasBackground?: boolean;\n document: ReturnType<UseDocument>;\n}\n\nconst FormLayout = React.memo(({ layout, document, hasBackground = true }: FormLayoutProps) => {\n const { formatMessage } = useIntl();\n const modelUid = document.schema?.uid;\n\n const getLabel = (name: string, label: string) => {\n return formatMessage({\n id: `content-manager.content-types.${modelUid}.${name}`,\n defaultMessage: label,\n });\n };\n\n return (\n <Flex direction=\"column\" alignItems=\"stretch\" gap={6}>\n {layout.map((panel) => {\n if (panel.some((row) => row.some((field) => field.type === 'dynamiczone'))) {\n const [row] = panel;\n const [field] = row;\n\n return (\n <Grid.Root key={field.name} gap={4}>\n <Grid.Item col={12} s={12} xs={12} direction=\"column\" alignItems=\"stretch\">\n <InputRenderer\n {...field}\n label={getLabel(field.name, field.label)}\n document={document}\n />\n </Grid.Item>\n </Grid.Root>\n );\n }\n\n return (\n <Box\n key={panel.flatMap((row) => row.map((field) => field.name)).join('.')}\n {...(hasBackground && panelStyles)}\n >\n <Flex direction=\"column\" alignItems=\"stretch\" gap={6}>\n {panel.map((row) => {\n return (\n <ResponsiveGridRoot\n key={row.map((field) => field.name).join('.')}\n gap={{ initial: 6, medium: 4 }}\n >\n {row.map(({ size, ...field }) => {\n return (\n <ResponsiveGridItem\n col={size}\n key={field.name}\n s={12}\n xs={12}\n direction=\"column\"\n alignItems=\"stretch\"\n >\n <InputRenderer\n {...field}\n size={size}\n label={getLabel(field.name, field.label)}\n document={document}\n />\n </ResponsiveGridItem>\n );\n })}\n </ResponsiveGridRoot>\n );\n })}\n </Flex>\n </Box>\n );\n })}\n </Flex>\n );\n});\n\nFormLayout.displayName = 'FormLayout';\n\nexport { FormLayout, FormLayoutProps };\n"],"names":["ResponsiveGridRoot","styled","Grid","Root","ResponsiveGridItem","process","env","NODE_ENV","Item","theme","breakpoints","medium","col","panelStyles","padding","initial","borderColor","background","hasRadius","shadow","FormLayout","React","memo","layout","document","hasBackground","formatMessage","useIntl","modelUid","schema","uid","getLabel","name","label","id","defaultMessage","_jsx","Flex","direction","alignItems","gap","map","panel","some","row","field","type","s","xs","InputRenderer","Box","size","join","flatMap","displayName"],"mappings":"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"}
1
+ {"version":3,"file":"FormLayout.js","sources":["../../../../../admin/src/pages/EditView/components/FormLayout.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useForm } from '@strapi/admin/strapi-admin';\nimport { Box, BoxProps, Flex, Grid } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { EditLayout } from '../../../hooks/useDocumentLayout';\nimport { NEAR_VIEWPORT_MARGIN, getScrollParent } from '../../../hooks/useHasBeenNearViewport';\n\nimport { InputRenderer } from './InputRenderer';\n\nimport type { UseDocument } from '../../../hooks/useDocument';\n\n/* -------------------------------------------------------------------------------------------------\n * LazyFormRow\n * -----------------------------------------------------------------------------------------------*/\n\ntype LayoutField = EditLayout['layout'][number][number][number];\n\n/**\n * Rough rendered heights, only used to size placeholders so the scrollbar stays sensible.\n */\nconst ESTIMATED_HEIGHT: Partial<Record<LayoutField['type'], number>> = {\n richtext: 320,\n blocks: 320,\n json: 240,\n component: 240,\n dynamiczone: 160,\n relation: 120,\n media: 160,\n text: 120,\n};\nconst DEFAULT_ESTIMATED_HEIGHT = 80;\n\n/* -------------------------------------------------------------------------------------------------\n * Idle mount queue\n * -----------------------------------------------------------------------------------------------*/\n\ntype ScheduleMount = (mount: () => void) => () => void;\n\ninterface IdleMountQueue {\n schedule: ScheduleMount;\n dispose: () => void;\n}\n\nconst requestIdle = (callback: () => void): number =>\n typeof window.requestIdleCallback === 'function'\n ? window.requestIdleCallback(callback, { timeout: 2000 })\n : window.setTimeout(callback, 16);\n\nconst cancelIdle = (handle: number) =>\n typeof window.cancelIdleCallback === 'function'\n ? window.cancelIdleCallback(handle)\n : window.clearTimeout(handle);\n\n/**\n * Mounts the rows that weren't needed for the first paint, one per idle period, in the order\n * they were scheduled (top to bottom). Rows are then already rendered when the user scrolls to\n * them, and the browser search finds every field.\n */\nconst createIdleMountQueue = (): IdleMountQueue => {\n let queue: Array<() => void> = [];\n let handle: number | null = null;\n\n const flush = () => {\n handle = null;\n queue.shift()?.();\n\n if (queue.length > 0) {\n handle = requestIdle(flush);\n }\n };\n\n return {\n schedule: (mount) => {\n queue.push(mount);\n\n if (handle === null) {\n handle = requestIdle(flush);\n }\n\n return () => {\n queue = queue.filter((scheduled) => scheduled !== mount);\n };\n },\n dispose: () => {\n queue = [];\n\n if (handle !== null) {\n cancelIdle(handle);\n handle = null;\n }\n },\n };\n};\n\nconst IdleMountContext = React.createContext<ScheduleMount | null>(null);\n\nconst useIdleMountQueue = () => {\n const [queue] = React.useState(createIdleMountQueue);\n\n React.useEffect(() => queue.dispose, [queue]);\n\n return queue.schedule;\n};\n\n/* -------------------------------------------------------------------------------------------------\n * LazyFormRow\n * -----------------------------------------------------------------------------------------------*/\n\ntype Components = ReturnType<UseDocument>['components'];\ntype Attribute = LayoutField['attribute'];\n\n/**\n * Some inputs set a value when they mount: a required UID fetches its default value, and custom\n * fields may set a default. Their rows are mounted with the page, as they always were, so that\n * saving right after the page loads doesn't miss those values. Non-repeatable components are\n * expanded by default, so the inputs they contain count too.\n */\nconst hasMountSideEffects = (attribute: Attribute, components: Components, depth = 0): boolean => {\n if ((attribute.type === 'uid' && attribute.required) || 'customField' in attribute) {\n return true;\n }\n\n if (attribute.type === 'component' && !attribute.repeatable && depth < 10) {\n return Object.values(components[attribute.component]?.attributes ?? {}).some((nested) =>\n hasMountSideEffects(nested as Attribute, components, depth + 1)\n );\n }\n\n return false;\n};\n\ninterface LazyFormRowProps {\n fields: Pick<LayoutField, 'name' | 'type'>[];\n mountImmediately?: boolean;\n children: React.ReactNode;\n}\n\n/**\n * Mounts its children once they get close to the viewport, or in the background when the\n * browser is idle, then keeps them mounted. Rendering every field of a large content type\n * upfront is what makes the edit view slow: the cost of the first render should depend on\n * the screen, not on the size of the model.\n */\nconst LazyFormRow = ({ fields, mountImmediately = false, children }: LazyFormRowProps) => {\n const scheduleMount = React.useContext(IdleMountContext);\n const placeholderRef = React.useRef<HTMLDivElement>(null);\n const [isVisible, setIsVisible] = React.useState(\n () => mountImmediately || typeof IntersectionObserver === 'undefined'\n );\n\n // A field in error has to be mounted so the error is displayed and can receive the focus.\n const hasError = useForm('LazyFormRow', (state) =>\n fields.some(({ name }) => name in state.errors)\n );\n\n // Once mounted for an error, the row stays mounted when the errors are cleared (every\n // validation starts by clearing them), so its state (e.g. open blocks) is preserved.\n React.useEffect(() => {\n if (hasError) {\n setIsVisible(true);\n }\n }, [hasError]);\n\n const shouldMount = isVisible || hasError;\n\n React.useEffect(() => {\n const placeholder = placeholderRef.current;\n\n if (shouldMount || !placeholder) {\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n setIsVisible(true);\n }\n },\n { root: getScrollParent(placeholder), rootMargin: NEAR_VIEWPORT_MARGIN }\n );\n\n observer.observe(placeholder);\n\n // Low priority: user input interrupts the render of a row mounted in the background.\n const cancelIdleMount = scheduleMount?.(() =>\n React.startTransition(() => {\n setIsVisible(true);\n })\n );\n\n return () => {\n observer.disconnect();\n cancelIdleMount?.();\n };\n }, [shouldMount, scheduleMount]);\n\n if (shouldMount) {\n return <>{children}</>;\n }\n\n const height = Math.max(\n ...fields.map(({ type }) => ESTIMATED_HEIGHT[type] ?? DEFAULT_ESTIMATED_HEIGHT)\n );\n\n return <div ref={placeholderRef} style={{ height }} aria-hidden />;\n};\n\nexport const ResponsiveGridRoot = styled(Grid.Root)`\n container-type: inline-size;\n`;\n\nexport const ResponsiveGridItem =\n /**\n * TODO:\n * JSDOM cannot handle container queries.\n * This is a temporary workaround so that tests do not fail in the CI when jestdom throws an error\n * for failing to parse the stylesheet.\n */\n process.env.NODE_ENV !== 'test'\n ? styled(Grid.Item)<{ col: number }>`\n grid-column: span 12;\n ${({ theme }) => theme.breakpoints.medium} {\n ${({ col }) => col && `grid-column: span ${col};`}\n }\n `\n : styled(Grid.Item)<{ col: number }>`\n grid-column: span 12;\n `;\n\nconst panelStyles = {\n padding: {\n initial: 4,\n medium: 6,\n },\n borderColor: 'neutral150',\n background: 'neutral0',\n hasRadius: true,\n shadow: 'tableShadow',\n} satisfies BoxProps;\n\ninterface FormLayoutProps extends Pick<EditLayout, 'layout'> {\n hasBackground?: boolean;\n document: ReturnType<UseDocument>;\n}\n\nconst FormLayout = React.memo(({ layout, document, hasBackground = true }: FormLayoutProps) => {\n const { formatMessage } = useIntl();\n const scheduleMount = useIdleMountQueue();\n const modelUid = document.schema?.uid;\n\n const getLabel = (name: string, label: string) => {\n return formatMessage({\n id: `content-manager.content-types.${modelUid}.${name}`,\n defaultMessage: label,\n });\n };\n\n return (\n <IdleMountContext.Provider value={scheduleMount}>\n <Flex direction=\"column\" alignItems=\"stretch\" gap={6}>\n {layout.map((panel) => {\n if (panel.some((row) => row.some((field) => field.type === 'dynamiczone'))) {\n const [row] = panel;\n const [field] = row;\n\n return (\n <LazyFormRow\n key={field.name}\n fields={[field]}\n mountImmediately={hasMountSideEffects(field.attribute, document.components)}\n >\n <Grid.Root gap={4}>\n <Grid.Item col={12} s={12} xs={12} direction=\"column\" alignItems=\"stretch\">\n <InputRenderer\n {...field}\n label={getLabel(field.name, field.label)}\n document={document}\n />\n </Grid.Item>\n </Grid.Root>\n </LazyFormRow>\n );\n }\n\n return (\n <Box\n key={panel.flatMap((row) => row.map((field) => field.name)).join('.')}\n {...(hasBackground && panelStyles)}\n >\n <Flex direction=\"column\" alignItems=\"stretch\" gap={6}>\n {panel.map((row) => {\n return (\n <LazyFormRow\n key={row.map((field) => field.name).join('.')}\n fields={row}\n mountImmediately={row.some((field) =>\n hasMountSideEffects(field.attribute, document.components)\n )}\n >\n <ResponsiveGridRoot gap={{ initial: 6, medium: 4 }}>\n {row.map(({ size, ...field }) => {\n return (\n <ResponsiveGridItem\n col={size}\n key={field.name}\n s={12}\n xs={12}\n direction=\"column\"\n alignItems=\"stretch\"\n >\n <InputRenderer\n {...field}\n size={size}\n label={getLabel(field.name, field.label)}\n document={document}\n />\n </ResponsiveGridItem>\n );\n })}\n </ResponsiveGridRoot>\n </LazyFormRow>\n );\n })}\n </Flex>\n </Box>\n );\n })}\n </Flex>\n </IdleMountContext.Provider>\n );\n});\n\nFormLayout.displayName = 'FormLayout';\n\nexport { FormLayout, FormLayoutProps, hasMountSideEffects };\n"],"names":["ESTIMATED_HEIGHT","richtext","blocks","json","component","dynamiczone","relation","media","text","DEFAULT_ESTIMATED_HEIGHT","requestIdle","callback","window","requestIdleCallback","timeout","setTimeout","cancelIdle","handle","cancelIdleCallback","clearTimeout","createIdleMountQueue","queue","flush","shift","length","schedule","mount","push","filter","scheduled","dispose","IdleMountContext","React","createContext","useIdleMountQueue","useState","useEffect","hasMountSideEffects","attribute","components","depth","type","required","repeatable","Object","values","attributes","some","nested","LazyFormRow","fields","mountImmediately","children","scheduleMount","useContext","placeholderRef","useRef","isVisible","setIsVisible","IntersectionObserver","hasError","useForm","state","name","errors","shouldMount","placeholder","current","observer","entries","entry","isIntersecting","root","getScrollParent","rootMargin","NEAR_VIEWPORT_MARGIN","observe","cancelIdleMount","startTransition","disconnect","_jsx","_Fragment","height","Math","max","map","div","ref","style","aria-hidden","ResponsiveGridRoot","styled","Grid","Root","ResponsiveGridItem","process","env","NODE_ENV","Item","theme","breakpoints","medium","col","panelStyles","padding","initial","borderColor","background","hasRadius","shadow","FormLayout","memo","layout","document","hasBackground","formatMessage","useIntl","modelUid","schema","uid","getLabel","label","id","defaultMessage","Provider","value","Flex","direction","alignItems","gap","panel","row","field","s","xs","InputRenderer","Box","size","join","flatMap","displayName"],"mappings":"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"}
@@ -1,10 +1,145 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
+ import { useForm } from '@strapi/admin/strapi-admin';
3
4
  import { Grid, Flex, Box } from '@strapi/design-system';
4
5
  import { useIntl } from 'react-intl';
5
6
  import { styled } from 'styled-components';
7
+ import { NEAR_VIEWPORT_MARGIN, getScrollParent } from '../../../hooks/useHasBeenNearViewport.mjs';
6
8
  import { InputRenderer as MemoizedInputRenderer } from './InputRenderer.mjs';
7
9
 
10
+ /**
11
+ * Rough rendered heights, only used to size placeholders so the scrollbar stays sensible.
12
+ */ const ESTIMATED_HEIGHT = {
13
+ richtext: 320,
14
+ blocks: 320,
15
+ json: 240,
16
+ component: 240,
17
+ dynamiczone: 160,
18
+ relation: 120,
19
+ media: 160,
20
+ text: 120
21
+ };
22
+ const DEFAULT_ESTIMATED_HEIGHT = 80;
23
+ const requestIdle = (callback)=>typeof window.requestIdleCallback === 'function' ? window.requestIdleCallback(callback, {
24
+ timeout: 2000
25
+ }) : window.setTimeout(callback, 16);
26
+ const cancelIdle = (handle)=>typeof window.cancelIdleCallback === 'function' ? window.cancelIdleCallback(handle) : window.clearTimeout(handle);
27
+ /**
28
+ * Mounts the rows that weren't needed for the first paint, one per idle period, in the order
29
+ * they were scheduled (top to bottom). Rows are then already rendered when the user scrolls to
30
+ * them, and the browser search finds every field.
31
+ */ const createIdleMountQueue = ()=>{
32
+ let queue = [];
33
+ let handle = null;
34
+ const flush = ()=>{
35
+ handle = null;
36
+ queue.shift()?.();
37
+ if (queue.length > 0) {
38
+ handle = requestIdle(flush);
39
+ }
40
+ };
41
+ return {
42
+ schedule: (mount)=>{
43
+ queue.push(mount);
44
+ if (handle === null) {
45
+ handle = requestIdle(flush);
46
+ }
47
+ return ()=>{
48
+ queue = queue.filter((scheduled)=>scheduled !== mount);
49
+ };
50
+ },
51
+ dispose: ()=>{
52
+ queue = [];
53
+ if (handle !== null) {
54
+ cancelIdle(handle);
55
+ handle = null;
56
+ }
57
+ }
58
+ };
59
+ };
60
+ const IdleMountContext = /*#__PURE__*/ React.createContext(null);
61
+ const useIdleMountQueue = ()=>{
62
+ const [queue] = React.useState(createIdleMountQueue);
63
+ React.useEffect(()=>queue.dispose, [
64
+ queue
65
+ ]);
66
+ return queue.schedule;
67
+ };
68
+ /**
69
+ * Some inputs set a value when they mount: a required UID fetches its default value, and custom
70
+ * fields may set a default. Their rows are mounted with the page, as they always were, so that
71
+ * saving right after the page loads doesn't miss those values. Non-repeatable components are
72
+ * expanded by default, so the inputs they contain count too.
73
+ */ const hasMountSideEffects = (attribute, components, depth = 0)=>{
74
+ if (attribute.type === 'uid' && attribute.required || 'customField' in attribute) {
75
+ return true;
76
+ }
77
+ if (attribute.type === 'component' && !attribute.repeatable && depth < 10) {
78
+ return Object.values(components[attribute.component]?.attributes ?? {}).some((nested)=>hasMountSideEffects(nested, components, depth + 1));
79
+ }
80
+ return false;
81
+ };
82
+ /**
83
+ * Mounts its children once they get close to the viewport, or in the background when the
84
+ * browser is idle, then keeps them mounted. Rendering every field of a large content type
85
+ * upfront is what makes the edit view slow: the cost of the first render should depend on
86
+ * the screen, not on the size of the model.
87
+ */ const LazyFormRow = ({ fields, mountImmediately = false, children })=>{
88
+ const scheduleMount = React.useContext(IdleMountContext);
89
+ const placeholderRef = React.useRef(null);
90
+ const [isVisible, setIsVisible] = React.useState(()=>mountImmediately || typeof IntersectionObserver === 'undefined');
91
+ // A field in error has to be mounted so the error is displayed and can receive the focus.
92
+ const hasError = useForm('LazyFormRow', (state)=>fields.some(({ name })=>name in state.errors));
93
+ // Once mounted for an error, the row stays mounted when the errors are cleared (every
94
+ // validation starts by clearing them), so its state (e.g. open blocks) is preserved.
95
+ React.useEffect(()=>{
96
+ if (hasError) {
97
+ setIsVisible(true);
98
+ }
99
+ }, [
100
+ hasError
101
+ ]);
102
+ const shouldMount = isVisible || hasError;
103
+ React.useEffect(()=>{
104
+ const placeholder = placeholderRef.current;
105
+ if (shouldMount || !placeholder) {
106
+ return;
107
+ }
108
+ const observer = new IntersectionObserver((entries)=>{
109
+ if (entries.some((entry)=>entry.isIntersecting)) {
110
+ setIsVisible(true);
111
+ }
112
+ }, {
113
+ root: getScrollParent(placeholder),
114
+ rootMargin: NEAR_VIEWPORT_MARGIN
115
+ });
116
+ observer.observe(placeholder);
117
+ // Low priority: user input interrupts the render of a row mounted in the background.
118
+ const cancelIdleMount = scheduleMount?.(()=>React.startTransition(()=>{
119
+ setIsVisible(true);
120
+ }));
121
+ return ()=>{
122
+ observer.disconnect();
123
+ cancelIdleMount?.();
124
+ };
125
+ }, [
126
+ shouldMount,
127
+ scheduleMount
128
+ ]);
129
+ if (shouldMount) {
130
+ return /*#__PURE__*/ jsx(Fragment, {
131
+ children: children
132
+ });
133
+ }
134
+ const height = Math.max(...fields.map(({ type })=>ESTIMATED_HEIGHT[type] ?? DEFAULT_ESTIMATED_HEIGHT));
135
+ return /*#__PURE__*/ jsx("div", {
136
+ ref: placeholderRef,
137
+ style: {
138
+ height
139
+ },
140
+ "aria-hidden": true
141
+ });
142
+ };
8
143
  const ResponsiveGridRoot = styled(Grid.Root)`
9
144
  container-type: inline-size;
10
145
  `;
@@ -33,6 +168,7 @@ const panelStyles = {
33
168
  };
34
169
  const FormLayout = /*#__PURE__*/ React.memo(({ layout, document, hasBackground = true })=>{
35
170
  const { formatMessage } = useIntl();
171
+ const scheduleMount = useIdleMountQueue();
36
172
  const modelUid = document.schema?.uid;
37
173
  const getLabel = (name, label)=>{
38
174
  return formatMessage({
@@ -40,65 +176,78 @@ const FormLayout = /*#__PURE__*/ React.memo(({ layout, document, hasBackground =
40
176
  defaultMessage: label
41
177
  });
42
178
  };
43
- return /*#__PURE__*/ jsx(Flex, {
44
- direction: "column",
45
- alignItems: "stretch",
46
- gap: 6,
47
- children: layout.map((panel)=>{
48
- if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
49
- const [row] = panel;
50
- const [field] = row;
51
- return /*#__PURE__*/ jsx(Grid.Root, {
52
- gap: 4,
53
- children: /*#__PURE__*/ jsx(Grid.Item, {
54
- col: 12,
55
- s: 12,
56
- xs: 12,
179
+ return /*#__PURE__*/ jsx(IdleMountContext.Provider, {
180
+ value: scheduleMount,
181
+ children: /*#__PURE__*/ jsx(Flex, {
182
+ direction: "column",
183
+ alignItems: "stretch",
184
+ gap: 6,
185
+ children: layout.map((panel)=>{
186
+ if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
187
+ const [row] = panel;
188
+ const [field] = row;
189
+ return /*#__PURE__*/ jsx(LazyFormRow, {
190
+ fields: [
191
+ field
192
+ ],
193
+ mountImmediately: hasMountSideEffects(field.attribute, document.components),
194
+ children: /*#__PURE__*/ jsx(Grid.Root, {
195
+ gap: 4,
196
+ children: /*#__PURE__*/ jsx(Grid.Item, {
197
+ col: 12,
198
+ s: 12,
199
+ xs: 12,
200
+ direction: "column",
201
+ alignItems: "stretch",
202
+ children: /*#__PURE__*/ jsx(MemoizedInputRenderer, {
203
+ ...field,
204
+ label: getLabel(field.name, field.label),
205
+ document: document
206
+ })
207
+ })
208
+ })
209
+ }, field.name);
210
+ }
211
+ return /*#__PURE__*/ jsx(Box, {
212
+ ...hasBackground && panelStyles,
213
+ children: /*#__PURE__*/ jsx(Flex, {
57
214
  direction: "column",
58
215
  alignItems: "stretch",
59
- children: /*#__PURE__*/ jsx(MemoizedInputRenderer, {
60
- ...field,
61
- label: getLabel(field.name, field.label),
62
- document: document
63
- })
64
- })
65
- }, field.name);
66
- }
67
- return /*#__PURE__*/ jsx(Box, {
68
- ...hasBackground && panelStyles,
69
- children: /*#__PURE__*/ jsx(Flex, {
70
- direction: "column",
71
- alignItems: "stretch",
72
- gap: 6,
73
- children: panel.map((row)=>{
74
- return /*#__PURE__*/ jsx(ResponsiveGridRoot, {
75
- gap: {
76
- initial: 6,
77
- medium: 4
78
- },
79
- children: row.map(({ size, ...field })=>{
80
- return /*#__PURE__*/ jsx(ResponsiveGridItem, {
81
- col: size,
82
- s: 12,
83
- xs: 12,
84
- direction: "column",
85
- alignItems: "stretch",
86
- children: /*#__PURE__*/ jsx(MemoizedInputRenderer, {
87
- ...field,
88
- size: size,
89
- label: getLabel(field.name, field.label),
90
- document: document
216
+ gap: 6,
217
+ children: panel.map((row)=>{
218
+ return /*#__PURE__*/ jsx(LazyFormRow, {
219
+ fields: row,
220
+ mountImmediately: row.some((field)=>hasMountSideEffects(field.attribute, document.components)),
221
+ children: /*#__PURE__*/ jsx(ResponsiveGridRoot, {
222
+ gap: {
223
+ initial: 6,
224
+ medium: 4
225
+ },
226
+ children: row.map(({ size, ...field })=>{
227
+ return /*#__PURE__*/ jsx(ResponsiveGridItem, {
228
+ col: size,
229
+ s: 12,
230
+ xs: 12,
231
+ direction: "column",
232
+ alignItems: "stretch",
233
+ children: /*#__PURE__*/ jsx(MemoizedInputRenderer, {
234
+ ...field,
235
+ size: size,
236
+ label: getLabel(field.name, field.label),
237
+ document: document
238
+ })
239
+ }, field.name);
91
240
  })
92
- }, field.name);
93
- })
94
- }, row.map((field)=>field.name).join('.'));
241
+ })
242
+ }, row.map((field)=>field.name).join('.'));
243
+ })
95
244
  })
96
- })
97
- }, panel.flatMap((row)=>row.map((field)=>field.name)).join('.'));
245
+ }, panel.flatMap((row)=>row.map((field)=>field.name)).join('.'));
246
+ })
98
247
  })
99
248
  });
100
249
  });
101
250
  FormLayout.displayName = 'FormLayout';
102
251
 
103
- export { FormLayout, ResponsiveGridItem, ResponsiveGridRoot };
252
+ export { FormLayout, ResponsiveGridItem, ResponsiveGridRoot, hasMountSideEffects };
104
253
  //# sourceMappingURL=FormLayout.mjs.map