@wordpress/block-editor 14.7.1-next.082ed6819.0 → 14.8.1-next.a9f418477.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 (303) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/autocompleters/block.js +2 -4
  3. package/build/autocompleters/block.js.map +1 -1
  4. package/build/autocompleters/link.js +2 -4
  5. package/build/autocompleters/link.js.map +1 -1
  6. package/build/components/block-canvas/index.js +3 -6
  7. package/build/components/block-canvas/index.js.map +1 -1
  8. package/build/components/block-list/block.js +6 -5
  9. package/build/components/block-list/block.js.map +1 -1
  10. package/build/components/block-list/index.js +0 -1
  11. package/build/components/block-list/index.js.map +1 -1
  12. package/build/components/block-list/use-block-props/index.js +6 -2
  13. package/build/components/block-list/use-block-props/index.js.map +1 -1
  14. package/build/components/block-list/use-block-props/use-firefox-draggable-compatibility.js +87 -0
  15. package/build/components/block-list/use-block-props/use-firefox-draggable-compatibility.js.map +1 -0
  16. package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js +98 -5
  17. package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +1 -1
  18. package/build/components/block-lock/modal.js +4 -4
  19. package/build/components/block-lock/modal.js.map +1 -1
  20. package/build/components/block-parent-selector/index.js +2 -15
  21. package/build/components/block-parent-selector/index.js.map +1 -1
  22. package/build/components/block-patterns-list/index.js +13 -4
  23. package/build/components/block-patterns-list/index.js.map +1 -1
  24. package/build/components/block-popover/inbetween.js +4 -0
  25. package/build/components/block-popover/inbetween.js.map +1 -1
  26. package/build/components/block-settings-menu/block-settings-dropdown.js +7 -4
  27. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  28. package/build/components/block-settings-menu-controls/index.js +1 -1
  29. package/build/components/block-settings-menu-controls/index.js.map +1 -1
  30. package/build/components/block-switcher/index.js +12 -22
  31. package/build/components/block-switcher/index.js.map +1 -1
  32. package/build/components/block-switcher/use-transformed-patterns.js +0 -1
  33. package/build/components/block-switcher/use-transformed-patterns.js.map +1 -1
  34. package/build/components/block-switcher/utils.js +0 -1
  35. package/build/components/block-switcher/utils.js.map +1 -1
  36. package/build/components/block-toolbar/index.js +17 -9
  37. package/build/components/block-toolbar/index.js.map +1 -1
  38. package/build/components/block-variation-transforms/index.js +0 -1
  39. package/build/components/block-variation-transforms/index.js.map +1 -1
  40. package/build/components/date-format-picker/index.js +0 -1
  41. package/build/components/date-format-picker/index.js.map +1 -1
  42. package/build/components/font-appearance-control/index.js +1 -0
  43. package/build/components/font-appearance-control/index.js.map +1 -1
  44. package/build/components/font-family/index.js +10 -0
  45. package/build/components/font-family/index.js.map +1 -1
  46. package/build/components/global-styles/dimensions-panel.js +17 -16
  47. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  48. package/build/components/global-styles/get-global-styles-changes.js +0 -1
  49. package/build/components/global-styles/get-global-styles-changes.js.map +1 -1
  50. package/build/components/iframe/index.js +12 -216
  51. package/build/components/iframe/index.js.map +1 -1
  52. package/build/components/iframe/use-scale-canvas.js +398 -0
  53. package/build/components/iframe/use-scale-canvas.js.map +1 -0
  54. package/build/components/image-editor/use-save-image.js +22 -3
  55. package/build/components/image-editor/use-save-image.js.map +1 -1
  56. package/build/components/inserter/block-patterns-tab/index.js +0 -10
  57. package/build/components/inserter/block-patterns-tab/index.js.map +1 -1
  58. package/build/components/inserter/menu.js +2 -1
  59. package/build/components/inserter/menu.js.map +1 -1
  60. package/build/components/inserter-draggable-blocks/index.js +19 -10
  61. package/build/components/inserter-draggable-blocks/index.js.map +1 -1
  62. package/build/components/inspector-controls/slot.js +2 -13
  63. package/build/components/inspector-controls/slot.js.map +1 -1
  64. package/build/components/letter-spacing-control/index.js +10 -0
  65. package/build/components/letter-spacing-control/index.js.map +1 -1
  66. package/build/components/line-height-control/index.js +1 -0
  67. package/build/components/line-height-control/index.js.map +1 -1
  68. package/build/components/media-placeholder/index.js +18 -18
  69. package/build/components/media-placeholder/index.js.map +1 -1
  70. package/build/components/multi-selection-inspector/index.js +9 -25
  71. package/build/components/multi-selection-inspector/index.js.map +1 -1
  72. package/build/components/observe-typing/index.js +0 -1
  73. package/build/components/observe-typing/index.js.map +1 -1
  74. package/build/components/recursion-provider/index.js +0 -1
  75. package/build/components/recursion-provider/index.js.map +1 -1
  76. package/build/components/rich-text/index.js +5 -1
  77. package/build/components/rich-text/index.js.map +1 -1
  78. package/build/components/rich-text/native/use-format-types.js +0 -1
  79. package/build/components/rich-text/native/use-format-types.js.map +1 -1
  80. package/build/components/rich-text/use-format-types.js +0 -1
  81. package/build/components/rich-text/use-format-types.js.map +1 -1
  82. package/build/components/spacing-sizes-control/utils.js +0 -1
  83. package/build/components/spacing-sizes-control/utils.js.map +1 -1
  84. package/build/components/typewriter/index.js +0 -1
  85. package/build/components/typewriter/index.js.map +1 -1
  86. package/build/components/use-block-drop-zone/index.js +11 -2
  87. package/build/components/use-block-drop-zone/index.js.map +1 -1
  88. package/build/components/use-moving-animation/index.js +15 -2
  89. package/build/components/use-moving-animation/index.js.map +1 -1
  90. package/build/components/use-resize-canvas/index.js +1 -1
  91. package/build/components/use-resize-canvas/index.js.map +1 -1
  92. package/build/components/warning/index.js +2 -3
  93. package/build/components/warning/index.js.map +1 -1
  94. package/build/components/writing-flow/use-drag-selection.js +11 -0
  95. package/build/components/writing-flow/use-drag-selection.js.map +1 -1
  96. package/build/components/writing-flow/use-tab-nav.js +6 -2
  97. package/build/components/writing-flow/use-tab-nav.js.map +1 -1
  98. package/build/hooks/block-bindings.js +4 -3
  99. package/build/hooks/block-bindings.js.map +1 -1
  100. package/build/hooks/gap.js +1 -1
  101. package/build/hooks/gap.js.map +1 -1
  102. package/build/hooks/generated-class-name.js +0 -1
  103. package/build/hooks/generated-class-name.js.map +1 -1
  104. package/build/hooks/use-zoom-out.js +47 -14
  105. package/build/hooks/use-zoom-out.js.map +1 -1
  106. package/build/store/private-selectors.js +1 -7
  107. package/build/store/private-selectors.js.map +1 -1
  108. package/build/store/reducer.js +478 -2
  109. package/build/store/reducer.js.map +1 -1
  110. package/build/store/selectors.js +12 -55
  111. package/build/store/selectors.js.map +1 -1
  112. package/build/utils/object.js +0 -1
  113. package/build/utils/object.js.map +1 -1
  114. package/build-module/autocompleters/block.js +2 -4
  115. package/build-module/autocompleters/block.js.map +1 -1
  116. package/build-module/autocompleters/link.js +2 -4
  117. package/build-module/autocompleters/link.js.map +1 -1
  118. package/build-module/components/block-canvas/index.js +3 -6
  119. package/build-module/components/block-canvas/index.js.map +1 -1
  120. package/build-module/components/block-list/block.js +8 -7
  121. package/build-module/components/block-list/block.js.map +1 -1
  122. package/build-module/components/block-list/index.js +0 -1
  123. package/build-module/components/block-list/index.js.map +1 -1
  124. package/build-module/components/block-list/use-block-props/index.js +6 -2
  125. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  126. package/build-module/components/block-list/use-block-props/use-firefox-draggable-compatibility.js +80 -0
  127. package/build-module/components/block-list/use-block-props/use-firefox-draggable-compatibility.js.map +1 -0
  128. package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js +97 -5
  129. package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +1 -1
  130. package/build-module/components/block-lock/modal.js +4 -4
  131. package/build-module/components/block-lock/modal.js.map +1 -1
  132. package/build-module/components/block-parent-selector/index.js +2 -15
  133. package/build-module/components/block-parent-selector/index.js.map +1 -1
  134. package/build-module/components/block-patterns-list/index.js +13 -4
  135. package/build-module/components/block-patterns-list/index.js.map +1 -1
  136. package/build-module/components/block-popover/inbetween.js +4 -0
  137. package/build-module/components/block-popover/inbetween.js.map +1 -1
  138. package/build-module/components/block-settings-menu/block-settings-dropdown.js +7 -4
  139. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  140. package/build-module/components/block-settings-menu-controls/index.js +1 -1
  141. package/build-module/components/block-settings-menu-controls/index.js.map +1 -1
  142. package/build-module/components/block-switcher/index.js +13 -23
  143. package/build-module/components/block-switcher/index.js.map +1 -1
  144. package/build-module/components/block-switcher/use-transformed-patterns.js +0 -1
  145. package/build-module/components/block-switcher/use-transformed-patterns.js.map +1 -1
  146. package/build-module/components/block-switcher/utils.js +0 -1
  147. package/build-module/components/block-switcher/utils.js.map +1 -1
  148. package/build-module/components/block-toolbar/index.js +17 -9
  149. package/build-module/components/block-toolbar/index.js.map +1 -1
  150. package/build-module/components/block-variation-transforms/index.js +0 -1
  151. package/build-module/components/block-variation-transforms/index.js.map +1 -1
  152. package/build-module/components/date-format-picker/index.js +0 -1
  153. package/build-module/components/date-format-picker/index.js.map +1 -1
  154. package/build-module/components/font-appearance-control/index.js +1 -0
  155. package/build-module/components/font-appearance-control/index.js.map +1 -1
  156. package/build-module/components/font-family/index.js +10 -0
  157. package/build-module/components/font-family/index.js.map +1 -1
  158. package/build-module/components/global-styles/dimensions-panel.js +17 -16
  159. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  160. package/build-module/components/global-styles/get-global-styles-changes.js +0 -1
  161. package/build-module/components/global-styles/get-global-styles-changes.js.map +1 -1
  162. package/build-module/components/iframe/index.js +14 -218
  163. package/build-module/components/iframe/index.js.map +1 -1
  164. package/build-module/components/iframe/use-scale-canvas.js +392 -0
  165. package/build-module/components/iframe/use-scale-canvas.js.map +1 -0
  166. package/build-module/components/image-editor/use-save-image.js +22 -3
  167. package/build-module/components/image-editor/use-save-image.js.map +1 -1
  168. package/build-module/components/inserter/block-patterns-tab/index.js +1 -11
  169. package/build-module/components/inserter/block-patterns-tab/index.js.map +1 -1
  170. package/build-module/components/inserter/menu.js +2 -1
  171. package/build-module/components/inserter/menu.js.map +1 -1
  172. package/build-module/components/inserter-draggable-blocks/index.js +20 -11
  173. package/build-module/components/inserter-draggable-blocks/index.js.map +1 -1
  174. package/build-module/components/inspector-controls/slot.js +3 -14
  175. package/build-module/components/inspector-controls/slot.js.map +1 -1
  176. package/build-module/components/letter-spacing-control/index.js +9 -0
  177. package/build-module/components/letter-spacing-control/index.js.map +1 -1
  178. package/build-module/components/line-height-control/index.js +1 -0
  179. package/build-module/components/line-height-control/index.js.map +1 -1
  180. package/build-module/components/media-placeholder/index.js +18 -18
  181. package/build-module/components/media-placeholder/index.js.map +1 -1
  182. package/build-module/components/multi-selection-inspector/index.js +9 -25
  183. package/build-module/components/multi-selection-inspector/index.js.map +1 -1
  184. package/build-module/components/observe-typing/index.js +0 -1
  185. package/build-module/components/observe-typing/index.js.map +1 -1
  186. package/build-module/components/recursion-provider/index.js +0 -1
  187. package/build-module/components/recursion-provider/index.js.map +1 -1
  188. package/build-module/components/rich-text/index.js +5 -1
  189. package/build-module/components/rich-text/index.js.map +1 -1
  190. package/build-module/components/rich-text/native/use-format-types.js +0 -1
  191. package/build-module/components/rich-text/native/use-format-types.js.map +1 -1
  192. package/build-module/components/rich-text/use-format-types.js +0 -1
  193. package/build-module/components/rich-text/use-format-types.js.map +1 -1
  194. package/build-module/components/spacing-sizes-control/utils.js +0 -1
  195. package/build-module/components/spacing-sizes-control/utils.js.map +1 -1
  196. package/build-module/components/typewriter/index.js +0 -1
  197. package/build-module/components/typewriter/index.js.map +1 -1
  198. package/build-module/components/use-block-drop-zone/index.js +11 -2
  199. package/build-module/components/use-block-drop-zone/index.js.map +1 -1
  200. package/build-module/components/use-moving-animation/index.js +15 -2
  201. package/build-module/components/use-moving-animation/index.js.map +1 -1
  202. package/build-module/components/use-resize-canvas/index.js +1 -1
  203. package/build-module/components/use-resize-canvas/index.js.map +1 -1
  204. package/build-module/components/warning/index.js +2 -3
  205. package/build-module/components/warning/index.js.map +1 -1
  206. package/build-module/components/writing-flow/use-drag-selection.js +11 -0
  207. package/build-module/components/writing-flow/use-drag-selection.js.map +1 -1
  208. package/build-module/components/writing-flow/use-tab-nav.js +6 -2
  209. package/build-module/components/writing-flow/use-tab-nav.js.map +1 -1
  210. package/build-module/hooks/block-bindings.js +4 -3
  211. package/build-module/hooks/block-bindings.js.map +1 -1
  212. package/build-module/hooks/gap.js +1 -1
  213. package/build-module/hooks/gap.js.map +1 -1
  214. package/build-module/hooks/generated-class-name.js +0 -1
  215. package/build-module/hooks/generated-class-name.js.map +1 -1
  216. package/build-module/hooks/use-zoom-out.js +48 -15
  217. package/build-module/hooks/use-zoom-out.js.map +1 -1
  218. package/build-module/store/private-selectors.js +1 -6
  219. package/build-module/store/private-selectors.js.map +1 -1
  220. package/build-module/store/reducer.js +479 -3
  221. package/build-module/store/reducer.js.map +1 -1
  222. package/build-module/store/selectors.js +12 -55
  223. package/build-module/store/selectors.js.map +1 -1
  224. package/build-module/utils/object.js +0 -1
  225. package/build-module/utils/object.js.map +1 -1
  226. package/build-style/content-rtl.css +25 -27
  227. package/build-style/content.css +25 -27
  228. package/build-style/style-rtl.css +55 -64
  229. package/build-style/style.css +55 -64
  230. package/package.json +32 -32
  231. package/src/autocompleters/block.js +2 -4
  232. package/src/autocompleters/link.js +2 -4
  233. package/src/components/alignment-control/stories/aliginment-toolbar.story.js +47 -0
  234. package/src/components/alignment-control/stories/index.story.js +51 -0
  235. package/src/components/alignment-control/test/__snapshots__/index.js.snap +5 -5
  236. package/src/components/block-alignment-control/test/__snapshots__/index.js.snap +4 -4
  237. package/src/components/block-canvas/index.js +3 -5
  238. package/src/components/block-canvas/style.scss +2 -1
  239. package/src/components/block-draggable/content.scss +11 -5
  240. package/src/components/block-list/block.js +7 -13
  241. package/src/components/block-list/content.scss +6 -0
  242. package/src/components/block-list/use-block-props/index.js +5 -0
  243. package/src/components/block-list/use-block-props/use-firefox-draggable-compatibility.js +83 -0
  244. package/src/components/block-list/use-block-props/use-selected-block-event-handlers.js +112 -8
  245. package/src/components/block-lock/modal.js +4 -6
  246. package/src/components/block-parent-selector/index.js +1 -19
  247. package/src/components/block-patterns-list/index.js +12 -1
  248. package/src/components/block-patterns-list/stories/fixtures.js +1 -0
  249. package/src/components/block-patterns-list/style.scss +16 -5
  250. package/src/components/block-popover/inbetween.js +4 -0
  251. package/src/components/block-settings-menu/block-settings-dropdown.js +6 -1
  252. package/src/components/block-settings-menu-controls/index.js +2 -1
  253. package/src/components/block-switcher/index.js +19 -21
  254. package/src/components/block-switcher/style.scss +0 -9
  255. package/src/components/block-title/test/index.js +2 -0
  256. package/src/components/block-toolbar/index.js +17 -6
  257. package/src/components/block-tools/style.scss +44 -0
  258. package/src/components/block-vertical-alignment-control/test/__snapshots__/index.js.snap +3 -3
  259. package/src/components/color-palette/test/__snapshots__/control.js.snap +2 -2
  260. package/src/components/dimensions-tool/stories/aspect-ratio-tool.story.js +1 -1
  261. package/src/components/dimensions-tool/stories/index.story.js +1 -1
  262. package/src/components/dimensions-tool/stories/scale-tool.story.js +1 -1
  263. package/src/components/dimensions-tool/stories/width-height-tool.story.js +1 -1
  264. package/src/components/font-appearance-control/index.js +1 -0
  265. package/src/components/font-family/index.js +10 -0
  266. package/src/components/font-family/style.scss +5 -0
  267. package/src/components/global-styles/dimensions-panel.js +16 -16
  268. package/src/components/iframe/content.scss +40 -42
  269. package/src/components/iframe/index.js +13 -313
  270. package/src/components/iframe/use-scale-canvas.js +490 -0
  271. package/src/components/image-editor/use-save-image.js +27 -2
  272. package/src/components/inserter/block-patterns-tab/index.js +1 -17
  273. package/src/components/inserter/menu.js +8 -1
  274. package/src/components/inserter-draggable-blocks/index.js +19 -29
  275. package/src/components/inspector-controls/slot.js +3 -22
  276. package/src/components/letter-spacing-control/README.md +2 -1
  277. package/src/components/letter-spacing-control/index.js +17 -0
  278. package/src/components/line-height-control/index.js +1 -0
  279. package/src/components/media-placeholder/index.js +25 -28
  280. package/src/components/multi-selection-inspector/index.js +17 -27
  281. package/src/components/multi-selection-inspector/style.scss +0 -12
  282. package/src/components/resolution-tool/stories/index.story.js +1 -1
  283. package/src/components/rich-text/index.js +5 -0
  284. package/src/components/spacing-sizes-control/style.scss +0 -29
  285. package/src/components/text-alignment-control/stories/index.story.js +1 -1
  286. package/src/components/use-block-drop-zone/index.js +18 -1
  287. package/src/components/use-moving-animation/index.js +15 -0
  288. package/src/components/use-resize-canvas/index.js +1 -1
  289. package/src/components/warning/index.js +3 -4
  290. package/src/components/warning/test/index.js +3 -1
  291. package/src/components/writing-flow/use-drag-selection.js +11 -0
  292. package/src/components/writing-flow/use-tab-nav.js +9 -6
  293. package/src/hooks/block-bindings.js +8 -4
  294. package/src/hooks/gap.js +1 -1
  295. package/src/hooks/use-zoom-out.js +48 -16
  296. package/src/store/private-selectors.js +2 -17
  297. package/src/store/reducer.js +639 -2
  298. package/src/store/selectors.js +19 -69
  299. package/src/store/test/private-selectors.js +1 -0
  300. package/src/store/test/reducer.js +849 -0
  301. package/src/store/test/selectors.js +4 -110
  302. package/src/style.scss +1 -0
  303. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,490 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useEffect, useRef, useCallback } from '@wordpress/element';
5
+ import { useReducedMotion, useResizeObserver } from '@wordpress/compose';
6
+
7
+ /**
8
+ * @typedef {Object} TransitionState
9
+ * @property {number} scaleValue Scale of the canvas.
10
+ * @property {number} frameSize Size of the frame/offset around the canvas.
11
+ * @property {number} containerHeight containerHeight of the iframe.
12
+ * @property {number} scrollTop ScrollTop of the iframe.
13
+ * @property {number} scrollHeight ScrollHeight of the iframe.
14
+ */
15
+
16
+ /**
17
+ * Calculate the scale of the canvas.
18
+ *
19
+ * @param {Object} options Object of options
20
+ * @param {number} options.frameSize Size of the frame/offset around the canvas
21
+ * @param {number} options.containerWidth Actual width of the canvas container
22
+ * @param {number} options.maxContainerWidth Maximum width of the container to use for the scale calculation. This locks the canvas to a maximum width when zooming out.
23
+ * @param {number} options.scaleContainerWidth Width the of the container wrapping the canvas container
24
+ * @return {number} Scale value between 0 and/or equal to 1
25
+ */
26
+ function calculateScale( {
27
+ frameSize,
28
+ containerWidth,
29
+ maxContainerWidth,
30
+ scaleContainerWidth,
31
+ } ) {
32
+ return (
33
+ ( Math.min( containerWidth, maxContainerWidth ) - frameSize * 2 ) /
34
+ scaleContainerWidth
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Compute the next scrollHeight based on the transition states.
40
+ *
41
+ * @param {TransitionState} transitionFrom Starting point of the transition
42
+ * @param {TransitionState} transitionTo Ending state of the transition
43
+ * @return {number} Next scrollHeight based on scale and frame value changes.
44
+ */
45
+ function computeScrollHeightNext( transitionFrom, transitionTo ) {
46
+ const { scaleValue: prevScale, scrollHeight: prevScrollHeight } =
47
+ transitionFrom;
48
+ const { frameSize, scaleValue } = transitionTo;
49
+
50
+ return prevScrollHeight * ( scaleValue / prevScale ) + frameSize * 2;
51
+ }
52
+
53
+ /**
54
+ * Compute the next scrollTop position after scaling the iframe content.
55
+ *
56
+ * @param {TransitionState} transitionFrom Starting point of the transition
57
+ * @param {TransitionState} transitionTo Ending state of the transition
58
+ * @return {number} Next scrollTop position after scaling the iframe content.
59
+ */
60
+ function computeScrollTopNext( transitionFrom, transitionTo ) {
61
+ const {
62
+ containerHeight: prevContainerHeight,
63
+ frameSize: prevFrameSize,
64
+ scaleValue: prevScale,
65
+ scrollTop: prevScrollTop,
66
+ } = transitionFrom;
67
+ const { containerHeight, frameSize, scaleValue, scrollHeight } =
68
+ transitionTo;
69
+ // Step 0: Start with the current scrollTop.
70
+ let scrollTopNext = prevScrollTop;
71
+ // Step 1: Undo the effects of the previous scale and frame around the
72
+ // midpoint of the visible area.
73
+ scrollTopNext =
74
+ ( scrollTopNext + prevContainerHeight / 2 - prevFrameSize ) /
75
+ prevScale -
76
+ prevContainerHeight / 2;
77
+
78
+ // Step 2: Apply the new scale and frame around the midpoint of the
79
+ // visible area.
80
+ scrollTopNext =
81
+ ( scrollTopNext + containerHeight / 2 ) * scaleValue +
82
+ frameSize -
83
+ containerHeight / 2;
84
+
85
+ // Step 3: Handle an edge case so that you scroll to the top of the
86
+ // iframe if the top of the iframe content is visible in the container.
87
+ // The same edge case for the bottom is skipped because changing content
88
+ // makes calculating it impossible.
89
+ scrollTopNext = prevScrollTop <= prevFrameSize ? 0 : scrollTopNext;
90
+
91
+ // This is the scrollTop value if you are scrolled to the bottom of the
92
+ // iframe. We can't just let the browser handle it because we need to
93
+ // animate the scaling.
94
+ const maxScrollTop = scrollHeight - containerHeight;
95
+
96
+ // Step 4: Clamp the scrollTopNext between the minimum and maximum
97
+ // possible scrollTop positions. Round the value to avoid subpixel
98
+ // truncation by the browser which sometimes causes a 1px error.
99
+ return Math.round(
100
+ Math.min( Math.max( 0, scrollTopNext ), Math.max( 0, maxScrollTop ) )
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Generate the keyframes to use for the zoom out animation.
106
+ *
107
+ * @param {TransitionState} transitionFrom Starting transition state.
108
+ * @param {TransitionState} transitionTo Ending transition state.
109
+ * @return {Object[]} An array of keyframes to use for the animation.
110
+ */
111
+ function getAnimationKeyframes( transitionFrom, transitionTo ) {
112
+ const {
113
+ scaleValue: prevScale,
114
+ frameSize: prevFrameSize,
115
+ scrollTop,
116
+ } = transitionFrom;
117
+ const { scaleValue, frameSize, scrollTop: scrollTopNext } = transitionTo;
118
+
119
+ return [
120
+ {
121
+ translate: `0 0`,
122
+ scale: prevScale,
123
+ paddingTop: `${ prevFrameSize / prevScale }px`,
124
+ paddingBottom: `${ prevFrameSize / prevScale }px`,
125
+ },
126
+ {
127
+ translate: `0 ${ scrollTop - scrollTopNext }px`,
128
+ scale: scaleValue,
129
+ paddingTop: `${ frameSize / scaleValue }px`,
130
+ paddingBottom: `${ frameSize / scaleValue }px`,
131
+ },
132
+ ];
133
+ }
134
+
135
+ /**
136
+ * @typedef {Object} ScaleCanvasResult
137
+ * @property {boolean} isZoomedOut A boolean indicating if the canvas is zoomed out.
138
+ * @property {number} scaleContainerWidth The width of the container used to calculate the scale.
139
+ * @property {Object} contentResizeListener A resize observer for the content.
140
+ * @property {Object} containerResizeListener A resize observer for the container.
141
+ */
142
+
143
+ /**
144
+ * Handles scaling the canvas for the zoom out mode and animating between
145
+ * the states.
146
+ *
147
+ * @param {Object} options Object of options.
148
+ * @param {number} options.frameSize Size of the frame around the content.
149
+ * @param {Document} options.iframeDocument Document of the iframe.
150
+ * @param {number} options.maxContainerWidth Max width of the canvas to use as the starting scale point. Defaults to 750.
151
+ * @param {number|string} options.scale Scale of the canvas. Can be an decimal between 0 and 1, 1, or 'auto-scaled'.
152
+ * @return {ScaleCanvasResult} Properties of the result.
153
+ */
154
+ export function useScaleCanvas( {
155
+ frameSize,
156
+ iframeDocument,
157
+ maxContainerWidth = 750,
158
+ scale,
159
+ } ) {
160
+ const [ contentResizeListener, { height: contentHeight } ] =
161
+ useResizeObserver();
162
+ const [
163
+ containerResizeListener,
164
+ { width: containerWidth, height: containerHeight },
165
+ ] = useResizeObserver();
166
+
167
+ const initialContainerWidthRef = useRef( 0 );
168
+ const isZoomedOut = scale !== 1;
169
+ const prefersReducedMotion = useReducedMotion();
170
+ const isAutoScaled = scale === 'auto-scaled';
171
+ // Track if the animation should start when the useEffect runs.
172
+ const startAnimationRef = useRef( false );
173
+ // Track the animation so we know if we have an animation running,
174
+ // and can cancel it, reverse it, call a finish event, etc.
175
+ const animationRef = useRef( null );
176
+
177
+ useEffect( () => {
178
+ if ( ! isZoomedOut ) {
179
+ initialContainerWidthRef.current = containerWidth;
180
+ }
181
+ }, [ containerWidth, isZoomedOut ] );
182
+
183
+ const scaleContainerWidth = Math.max(
184
+ initialContainerWidthRef.current,
185
+ containerWidth
186
+ );
187
+
188
+ const scaleValue = isAutoScaled
189
+ ? calculateScale( {
190
+ frameSize,
191
+ containerWidth,
192
+ maxContainerWidth,
193
+ scaleContainerWidth,
194
+ } )
195
+ : scale;
196
+
197
+ /**
198
+ * The starting transition state for the zoom out animation.
199
+ * @type {import('react').RefObject<TransitionState>}
200
+ */
201
+ const transitionFromRef = useRef( {
202
+ scaleValue,
203
+ frameSize,
204
+ containerHeight: 0,
205
+ scrollTop: 0,
206
+ scrollHeight: 0,
207
+ } );
208
+
209
+ /**
210
+ * The ending transition state for the zoom out animation.
211
+ * @type {import('react').RefObject<TransitionState>}
212
+ */
213
+ const transitionToRef = useRef( {
214
+ scaleValue,
215
+ frameSize,
216
+ containerHeight: 0,
217
+ scrollTop: 0,
218
+ scrollHeight: 0,
219
+ } );
220
+
221
+ /**
222
+ * Start the zoom out animation. This sets the necessary CSS variables
223
+ * for animating the canvas and returns the Animation object.
224
+ *
225
+ * @return {Animation} The animation object for the zoom out animation.
226
+ */
227
+ const startZoomOutAnimation = useCallback( () => {
228
+ const { scrollTop } = transitionFromRef.current;
229
+ const { scrollTop: scrollTopNext } = transitionToRef.current;
230
+
231
+ iframeDocument.documentElement.style.setProperty(
232
+ '--wp-block-editor-iframe-zoom-out-scroll-top',
233
+ `${ scrollTop }px`
234
+ );
235
+
236
+ iframeDocument.documentElement.style.setProperty(
237
+ '--wp-block-editor-iframe-zoom-out-scroll-top-next',
238
+ `${ scrollTopNext }px`
239
+ );
240
+
241
+ // If the container has a scrolllbar, force a scrollbar to prevent the content from shifting while animating.
242
+ iframeDocument.documentElement.style.setProperty(
243
+ '--wp-block-editor-iframe-zoom-out-overflow-behavior',
244
+ transitionFromRef.current.scrollHeight ===
245
+ transitionFromRef.current.containerHeight
246
+ ? 'auto'
247
+ : 'scroll'
248
+ );
249
+
250
+ iframeDocument.documentElement.classList.add( 'zoom-out-animation' );
251
+
252
+ return iframeDocument.documentElement.animate(
253
+ getAnimationKeyframes(
254
+ transitionFromRef.current,
255
+ transitionToRef.current
256
+ ),
257
+ {
258
+ easing: 'cubic-bezier(0.46, 0.03, 0.52, 0.96)',
259
+ duration: 400,
260
+ }
261
+ );
262
+ }, [ iframeDocument ] );
263
+
264
+ /**
265
+ * Callback when the zoom out animation is finished.
266
+ * - Cleans up animations refs.
267
+ * - Adds final CSS vars for scale and frame size to preserve the state.
268
+ * - Removes the 'zoom-out-animation' class (which has the fixed positioning).
269
+ * - Sets the final scroll position after the canvas is no longer in fixed position.
270
+ * - Removes CSS vars related to the animation.
271
+ * - Sets the transitionFrom to the transitionTo state to be ready for the next animation.
272
+ */
273
+ const finishZoomOutAnimation = useCallback( () => {
274
+ startAnimationRef.current = false;
275
+ animationRef.current = null;
276
+
277
+ // Add our final scale and frame size now that the animation is done.
278
+ iframeDocument.documentElement.style.setProperty(
279
+ '--wp-block-editor-iframe-zoom-out-scale',
280
+ transitionToRef.current.scaleValue
281
+ );
282
+ iframeDocument.documentElement.style.setProperty(
283
+ '--wp-block-editor-iframe-zoom-out-frame-size',
284
+ `${ transitionToRef.current.frameSize }px`
285
+ );
286
+
287
+ iframeDocument.documentElement.classList.remove( 'zoom-out-animation' );
288
+
289
+ // Set the final scroll position that was just animated to.
290
+ // Disable reason: Eslint isn't smart enough to know that this is a
291
+ // DOM element. https://github.com/facebook/react/issues/31483
292
+ // eslint-disable-next-line react-compiler/react-compiler
293
+ iframeDocument.documentElement.scrollTop =
294
+ transitionToRef.current.scrollTop;
295
+
296
+ iframeDocument.documentElement.style.removeProperty(
297
+ '--wp-block-editor-iframe-zoom-out-scroll-top'
298
+ );
299
+ iframeDocument.documentElement.style.removeProperty(
300
+ '--wp-block-editor-iframe-zoom-out-scroll-top-next'
301
+ );
302
+ iframeDocument.documentElement.style.removeProperty(
303
+ '--wp-block-editor-iframe-zoom-out-overflow-behavior'
304
+ );
305
+
306
+ // Update previous values.
307
+ transitionFromRef.current = transitionToRef.current;
308
+ }, [ iframeDocument ] );
309
+
310
+ const previousIsZoomedOut = useRef( false );
311
+
312
+ /**
313
+ * Runs when zoom out mode is toggled, and sets the startAnimation flag
314
+ * so the animation will start when the next useEffect runs. We _only_
315
+ * want to animate when the zoom out mode is toggled, not when the scale
316
+ * changes due to the container resizing.
317
+ */
318
+ useEffect( () => {
319
+ const trigger =
320
+ iframeDocument && previousIsZoomedOut.current !== isZoomedOut;
321
+
322
+ previousIsZoomedOut.current = isZoomedOut;
323
+
324
+ if ( ! trigger ) {
325
+ return;
326
+ }
327
+
328
+ startAnimationRef.current = true;
329
+
330
+ if ( ! isZoomedOut ) {
331
+ return;
332
+ }
333
+
334
+ iframeDocument.documentElement.classList.add( 'is-zoomed-out' );
335
+ return () => {
336
+ iframeDocument.documentElement.classList.remove( 'is-zoomed-out' );
337
+ };
338
+ }, [ iframeDocument, isZoomedOut ] );
339
+
340
+ /**
341
+ * This handles:
342
+ * 1. Setting the correct scale and vars of the canvas when zoomed out
343
+ * 2. If zoom out mode has been toggled, runs the animation of zooming in/out
344
+ */
345
+ useEffect( () => {
346
+ if ( ! iframeDocument ) {
347
+ return;
348
+ }
349
+
350
+ // We need to update the appropriate scale to exit from. If sidebars have been opened since setting the
351
+ // original scale, we will snap to a much smaller scale due to the scale container immediately changing sizes when exiting.
352
+ if ( isAutoScaled && transitionFromRef.current.scaleValue !== 1 ) {
353
+ // We use containerWidth as the divisor, as scaleContainerWidth will always match the containerWidth when
354
+ // exiting.
355
+ transitionFromRef.current.scaleValue = calculateScale( {
356
+ frameSize: transitionFromRef.current.frameSize,
357
+ containerWidth,
358
+ maxContainerWidth,
359
+ scaleContainerWidth: containerWidth,
360
+ } );
361
+ }
362
+
363
+ if ( scaleValue < 1 ) {
364
+ // If we are not going to animate the transition, set the scale and frame size directly.
365
+ // If we are animating, these values will be set when the animation is finished.
366
+ // Example: Opening sidebars that reduce the scale of the canvas, but we don't want to
367
+ // animate the transition.
368
+ if ( ! startAnimationRef.current ) {
369
+ iframeDocument.documentElement.style.setProperty(
370
+ '--wp-block-editor-iframe-zoom-out-scale',
371
+ scaleValue
372
+ );
373
+ iframeDocument.documentElement.style.setProperty(
374
+ '--wp-block-editor-iframe-zoom-out-frame-size',
375
+ `${ frameSize }px`
376
+ );
377
+ }
378
+
379
+ iframeDocument.documentElement.style.setProperty(
380
+ '--wp-block-editor-iframe-zoom-out-content-height',
381
+ `${ contentHeight }px`
382
+ );
383
+
384
+ iframeDocument.documentElement.style.setProperty(
385
+ '--wp-block-editor-iframe-zoom-out-inner-height',
386
+ `${ containerHeight }px`
387
+ );
388
+
389
+ iframeDocument.documentElement.style.setProperty(
390
+ '--wp-block-editor-iframe-zoom-out-container-width',
391
+ `${ containerWidth }px`
392
+ );
393
+ iframeDocument.documentElement.style.setProperty(
394
+ '--wp-block-editor-iframe-zoom-out-scale-container-width',
395
+ `${ scaleContainerWidth }px`
396
+ );
397
+ }
398
+
399
+ /**
400
+ * Handle the zoom out animation:
401
+ *
402
+ * - Get the current scrollTop position.
403
+ * - Calculate where the same scroll position is after scaling.
404
+ * - Apply fixed positioning to the canvas with a transform offset
405
+ * to keep the canvas centered.
406
+ * - Animate the scale and padding to the new scale and frame size.
407
+ * - After the animation is complete, remove the fixed positioning
408
+ * and set the scroll position that keeps everything centered.
409
+ */
410
+ if ( startAnimationRef.current ) {
411
+ // Don't allow a new transition to start again unless it was started by the zoom out mode changing.
412
+ startAnimationRef.current = false;
413
+
414
+ /**
415
+ * If we already have an animation running, reverse it.
416
+ */
417
+ if ( animationRef.current ) {
418
+ animationRef.current.reverse();
419
+ // Swap the transition to/from refs so that we set the correct values when
420
+ // finishZoomOutAnimation runs.
421
+ const tempTransitionFrom = transitionFromRef.current;
422
+ const tempTransitionTo = transitionToRef.current;
423
+ transitionFromRef.current = tempTransitionTo;
424
+ transitionToRef.current = tempTransitionFrom;
425
+ } else {
426
+ /**
427
+ * Start a new zoom animation.
428
+ */
429
+
430
+ // We can't trust the set value from contentHeight, as it was measured
431
+ // before the zoom out mode was changed. After zoom out mode is changed,
432
+ // appenders may appear or disappear, so we need to get the height from
433
+ // the iframe at this point when we're about to animate the zoom out.
434
+ // The iframe scrollTop, scrollHeight, and clientHeight will all be
435
+ // the most accurate.
436
+ transitionFromRef.current.scrollTop =
437
+ iframeDocument.documentElement.scrollTop;
438
+ transitionFromRef.current.scrollHeight =
439
+ iframeDocument.documentElement.scrollHeight;
440
+ // Use containerHeight, as it's the previous container height before the zoom out animation starts.
441
+ transitionFromRef.current.containerHeight = containerHeight;
442
+
443
+ transitionToRef.current = {
444
+ scaleValue,
445
+ frameSize,
446
+ containerHeight:
447
+ iframeDocument.documentElement.clientHeight, // use clientHeight to get the actual height of the new container after zoom state changes have rendered, as it will be the most up-to-date.
448
+ };
449
+
450
+ transitionToRef.current.scrollHeight = computeScrollHeightNext(
451
+ transitionFromRef.current,
452
+ transitionToRef.current
453
+ );
454
+ transitionToRef.current.scrollTop = computeScrollTopNext(
455
+ transitionFromRef.current,
456
+ transitionToRef.current
457
+ );
458
+
459
+ animationRef.current = startZoomOutAnimation();
460
+
461
+ // If the user prefers reduced motion, finish the animation immediately and set the final state.
462
+ if ( prefersReducedMotion ) {
463
+ finishZoomOutAnimation();
464
+ } else {
465
+ animationRef.current.onfinish = finishZoomOutAnimation;
466
+ }
467
+ }
468
+ }
469
+ }, [
470
+ startZoomOutAnimation,
471
+ finishZoomOutAnimation,
472
+ prefersReducedMotion,
473
+ isAutoScaled,
474
+ scaleValue,
475
+ frameSize,
476
+ iframeDocument,
477
+ contentHeight,
478
+ containerWidth,
479
+ containerHeight,
480
+ maxContainerWidth,
481
+ scaleContainerWidth,
482
+ ] );
483
+
484
+ return {
485
+ isZoomedOut,
486
+ scaleContainerWidth,
487
+ contentResizeListener,
488
+ containerResizeListener,
489
+ };
490
+ }
@@ -10,6 +10,12 @@ import { __, sprintf } from '@wordpress/i18n';
10
10
  import { store as noticesStore } from '@wordpress/notices';
11
11
  import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
12
12
 
13
+ const messages = {
14
+ crop: __( 'Image cropped.' ),
15
+ rotate: __( 'Image rotated.' ),
16
+ cropAndRotate: __( 'Image cropped and rotated.' ),
17
+ };
18
+
13
19
  export default function useSaveImage( {
14
20
  crop,
15
21
  rotation,
@@ -18,7 +24,8 @@ export default function useSaveImage( {
18
24
  onSaveImage,
19
25
  onFinishEditing,
20
26
  } ) {
21
- const { createErrorNotice } = useDispatch( noticesStore );
27
+ const { createErrorNotice, createSuccessNotice } =
28
+ useDispatch( noticesStore );
22
29
  const [ isInProgress, setIsInProgress ] = useState( false );
23
30
 
24
31
  const cancel = useCallback( () => {
@@ -61,6 +68,9 @@ export default function useSaveImage( {
61
68
  return;
62
69
  }
63
70
 
71
+ const modifierType =
72
+ modifiers.length === 1 ? modifiers[ 0 ].type : 'cropAndRotate';
73
+
64
74
  apiFetch( {
65
75
  path: `/wp/v2/media/${ id }/edit`,
66
76
  method: 'POST',
@@ -71,11 +81,25 @@ export default function useSaveImage( {
71
81
  id: response.id,
72
82
  url: response.source_url,
73
83
  } );
84
+ createSuccessNotice( messages[ modifierType ], {
85
+ type: 'snackbar',
86
+ actions: [
87
+ {
88
+ label: __( 'Undo' ),
89
+ onClick: () => {
90
+ onSaveImage( {
91
+ id,
92
+ url,
93
+ } );
94
+ },
95
+ },
96
+ ],
97
+ } );
74
98
  } )
75
99
  .catch( ( error ) => {
76
100
  createErrorNotice(
77
101
  sprintf(
78
- /* translators: 1. Error message */
102
+ /* translators: %s: Error message. */
79
103
  __( 'Could not edit image. %s' ),
80
104
  stripHTML( error.message )
81
105
  ),
@@ -96,6 +120,7 @@ export default function useSaveImage( {
96
120
  url,
97
121
  onSaveImage,
98
122
  createErrorNotice,
123
+ createSuccessNotice,
99
124
  onFinishEditing,
100
125
  ] );
101
126
 
@@ -3,9 +3,8 @@
3
3
  */
4
4
  import { useState } from '@wordpress/element';
5
5
  import { useViewportMatch } from '@wordpress/compose';
6
- import { Button, Spinner } from '@wordpress/components';
6
+ import { Button } from '@wordpress/components';
7
7
  import { __ } from '@wordpress/i18n';
8
- import { useSelect } from '@wordpress/data';
9
8
 
10
9
  /**
11
10
  * Internal dependencies
@@ -16,8 +15,6 @@ import { PatternCategoryPreviews } from './pattern-category-previews';
16
15
  import { usePatternCategories } from './use-pattern-categories';
17
16
  import CategoryTabs from '../category-tabs';
18
17
  import InserterNoResults from '../no-results';
19
- import { store as blockEditorStore } from '../../../store';
20
- import { unlock } from '../../../lock-unlock';
21
18
 
22
19
  function BlockPatternsTab( {
23
20
  onSelectCategory,
@@ -31,19 +28,6 @@ function BlockPatternsTab( {
31
28
  const categories = usePatternCategories( rootClientId );
32
29
 
33
30
  const isMobile = useViewportMatch( 'medium', '<' );
34
- const isResolvingPatterns = useSelect(
35
- ( select ) =>
36
- unlock( select( blockEditorStore ) ).isResolvingPatterns(),
37
- []
38
- );
39
-
40
- if ( isResolvingPatterns ) {
41
- return (
42
- <div className="block-editor-inserter__patterns-loading">
43
- <Spinner />
44
- </div>
45
- );
46
- }
47
31
 
48
32
  if ( ! categories.length ) {
49
33
  return <InserterNoResults />;
@@ -58,6 +58,11 @@ function InserterMenu(
58
58
  ( select ) => unlock( select( blockEditorStore ) ).isZoomOut(),
59
59
  []
60
60
  );
61
+ const hasSectionRootClientId = useSelect(
62
+ ( select ) =>
63
+ !! unlock( select( blockEditorStore ) ).getSectionRootClientId(),
64
+ []
65
+ );
61
66
  const [ filterValue, setFilterValue, delayedFilterValue ] =
62
67
  useDebouncedInput( __experimentalFilterValue );
63
68
  const [ hoveredItem, setHoveredItem ] = useState( null );
@@ -81,7 +86,9 @@ function InserterMenu(
81
86
  const [ selectedTab, setSelectedTab ] = useState( getInitialTab() );
82
87
 
83
88
  const shouldUseZoomOut =
84
- selectedTab === 'patterns' || selectedTab === 'media';
89
+ hasSectionRootClientId &&
90
+ ( selectedTab === 'patterns' || selectedTab === 'media' );
91
+
85
92
  useZoomOut( shouldUseZoomOut && isLargeViewport );
86
93
 
87
94
  const [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ] =
@@ -2,12 +2,9 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { Draggable } from '@wordpress/components';
5
- import {
6
- createBlock,
7
- serialize,
8
- store as blocksStore,
9
- } from '@wordpress/blocks';
5
+ import { createBlock, store as blocksStore } from '@wordpress/blocks';
10
6
  import { useDispatch, useSelect } from '@wordpress/data';
7
+ import { useMemo } from '@wordpress/element';
11
8
 
12
9
  /**
13
10
  * Internal dependencies
@@ -24,20 +21,6 @@ const InserterDraggableBlocks = ( {
24
21
  children,
25
22
  pattern,
26
23
  } ) => {
27
- const transferData = {
28
- type: 'inserter',
29
- blocks,
30
- };
31
-
32
- const blocksContainMedia =
33
- blocks.filter(
34
- ( block ) =>
35
- ( block.name === 'core/image' ||
36
- block.name === 'core/audio' ||
37
- block.name === 'core/video' ) &&
38
- ( block.attributes.url || block.attributes.src )
39
- ).length > 0;
40
-
41
24
  const blockTypeIcon = useSelect(
42
25
  ( select ) => {
43
26
  const { getBlockType } = select( blocksStore );
@@ -52,6 +35,13 @@ const InserterDraggableBlocks = ( {
52
35
  useDispatch( blockEditorStore )
53
36
  );
54
37
 
38
+ const patternBlock = useMemo( () => {
39
+ return pattern?.type === INSERTER_PATTERN_TYPES.user &&
40
+ pattern?.syncStatus !== 'unsynced'
41
+ ? [ createBlock( 'core/block', { ref: pattern.id } ) ]
42
+ : undefined;
43
+ }, [ pattern?.type, pattern?.syncStatus, pattern?.id ] );
44
+
55
45
  if ( ! isEnabled ) {
56
46
  return children( {
57
47
  draggable: false,
@@ -60,21 +50,21 @@ const InserterDraggableBlocks = ( {
60
50
  } );
61
51
  }
62
52
 
53
+ const draggableBlocks = patternBlock ?? blocks;
63
54
  return (
64
55
  <Draggable
65
56
  __experimentalTransferDataType="wp-blocks"
66
- transferData={ transferData }
57
+ transferData={ { type: 'inserter', blocks: draggableBlocks } }
67
58
  onDragStart={ ( event ) => {
68
59
  startDragging();
69
- const parsedBlocks =
70
- pattern?.type === INSERTER_PATTERN_TYPES.user &&
71
- pattern?.syncStatus !== 'unsynced'
72
- ? [ createBlock( 'core/block', { ref: pattern.id } ) ]
73
- : blocks;
74
- event.dataTransfer.setData(
75
- blocksContainMedia ? 'default' : 'text/html',
76
- serialize( parsedBlocks )
77
- );
60
+ for ( const block of draggableBlocks ) {
61
+ const type = `wp-block:${ block.name }`;
62
+ // This will fill in the dataTransfer.types array so that
63
+ // the drop zone can check if the draggable is eligible.
64
+ // Unfortuantely, on drag start, we don't have access to the
65
+ // actual data, only the data keys/types.
66
+ event.dataTransfer.items.add( '', type );
67
+ }
78
68
  } }
79
69
  onDragEnd={ () => {
80
70
  stopDragging();