@wordpress/block-editor 16.0.0 → 16.1.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 (368) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +8 -0
  3. package/build/components/alignment-control/ui.cjs +2 -4
  4. package/build/components/alignment-control/ui.cjs.map +2 -2
  5. package/build/components/background-image-control/index.cjs +40 -10
  6. package/build/components/background-image-control/index.cjs.map +2 -2
  7. package/build/components/block-alignment-control/ui.cjs +5 -3
  8. package/build/components/block-alignment-control/ui.cjs.map +2 -2
  9. package/build/components/block-inspector/index.cjs +73 -46
  10. package/build/components/block-inspector/index.cjs.map +2 -2
  11. package/build/components/block-list/block-html.cjs +4 -1
  12. package/build/components/block-list/block-html.cjs.map +2 -2
  13. package/build/components/block-list/use-block-props/index.cjs +5 -2
  14. package/build/components/block-list/use-block-props/index.cjs.map +2 -2
  15. package/build/components/block-list/use-block-props/use-focus-handler.cjs +2 -3
  16. package/build/components/block-list/use-block-props/use-focus-handler.cjs.map +2 -2
  17. package/build/components/block-list/use-block-props/use-register-block-event-handlers.cjs +48 -0
  18. package/build/components/block-list/use-block-props/use-register-block-event-handlers.cjs.map +7 -0
  19. package/build/components/block-parent-selector/index.cjs +2 -2
  20. package/build/components/block-parent-selector/index.cjs.map +2 -2
  21. package/build/components/block-toolbar/switch-section-style.cjs +4 -2
  22. package/build/components/block-toolbar/switch-section-style.cjs.map +3 -3
  23. package/build/components/block-tools/empty-block-inserter.cjs +2 -7
  24. package/build/components/block-tools/empty-block-inserter.cjs.map +2 -2
  25. package/build/components/block-vertical-alignment-control/ui.cjs +3 -5
  26. package/build/components/block-vertical-alignment-control/ui.cjs.map +2 -2
  27. package/build/components/border-radius-control/utils.cjs +8 -58
  28. package/build/components/border-radius-control/utils.cjs.map +2 -2
  29. package/build/components/colors-gradients/control.cjs +5 -3
  30. package/build/components/colors-gradients/control.cjs.map +2 -2
  31. package/build/components/dimension-control/index.cjs +30 -20
  32. package/build/components/dimension-control/index.cjs.map +3 -3
  33. package/build/components/dimensions-tool/aspect-ratio-tool.cjs +9 -2
  34. package/build/components/dimensions-tool/aspect-ratio-tool.cjs.map +3 -3
  35. package/build/components/editor-styles/index.cjs +9 -1
  36. package/build/components/editor-styles/index.cjs.map +2 -2
  37. package/build/components/global-styles/background-panel.cjs +66 -25
  38. package/build/components/global-styles/background-panel.cjs.map +3 -3
  39. package/build/components/global-styles/border-panel.cjs +153 -55
  40. package/build/components/global-styles/border-panel.cjs.map +3 -3
  41. package/build/components/global-styles/color-gradient-dropdown-item.cjs +46 -14
  42. package/build/components/global-styles/color-gradient-dropdown-item.cjs.map +3 -3
  43. package/build/components/global-styles/color-panel.cjs +63 -17
  44. package/build/components/global-styles/color-panel.cjs.map +2 -2
  45. package/build/components/global-styles/dimensions-panel.cjs +242 -64
  46. package/build/components/global-styles/dimensions-panel.cjs.map +3 -3
  47. package/build/components/global-styles/filters-panel.cjs +59 -20
  48. package/build/components/global-styles/filters-panel.cjs.map +3 -3
  49. package/build/components/global-styles/hooks.cjs +4 -1
  50. package/build/components/global-styles/hooks.cjs.map +2 -2
  51. package/build/components/global-styles/inheritance/index.cjs +142 -0
  52. package/build/components/global-styles/inheritance/index.cjs.map +7 -0
  53. package/build/components/global-styles/inherited-value-context.cjs +173 -0
  54. package/build/components/global-styles/inherited-value-context.cjs.map +7 -0
  55. package/build/components/global-styles/shadow-panel-components.cjs +38 -20
  56. package/build/components/global-styles/shadow-panel-components.cjs.map +2 -2
  57. package/build/components/global-styles/state-control.cjs +1 -1
  58. package/build/components/global-styles/state-control.cjs.map +2 -2
  59. package/build/components/global-styles/typography-panel.cjs +271 -78
  60. package/build/components/global-styles/typography-panel.cjs.map +3 -3
  61. package/build/components/inspector-controls-tabs/styles-tab.cjs +10 -1
  62. package/build/components/inspector-controls-tabs/styles-tab.cjs.map +2 -2
  63. package/build/components/line-height-control/index.cjs +1 -1
  64. package/build/components/line-height-control/index.cjs.map +2 -2
  65. package/build/components/link-control/index.cjs +3 -1
  66. package/build/components/link-control/index.cjs.map +2 -2
  67. package/build/components/preset-input-control/index.cjs +2 -1
  68. package/build/components/preset-input-control/index.cjs.map +2 -2
  69. package/build/components/provider/block-refs-provider.cjs +8 -2
  70. package/build/components/provider/block-refs-provider.cjs.map +2 -2
  71. package/build/components/provider/index.cjs +1 -1
  72. package/build/components/provider/index.cjs.map +2 -2
  73. package/build/components/provider/use-block-sync.cjs +18 -6
  74. package/build/components/provider/use-block-sync.cjs.map +2 -2
  75. package/build/components/provider/use-media-upload-settings.cjs +3 -1
  76. package/build/components/provider/use-media-upload-settings.cjs.map +2 -2
  77. package/build/components/rich-text/index.cjs +23 -13
  78. package/build/components/rich-text/index.cjs.map +2 -2
  79. package/build/components/spacing-sizes-control/utils.cjs +6 -19
  80. package/build/components/spacing-sizes-control/utils.cjs.map +2 -2
  81. package/build/components/text-indent-control/index.cjs +8 -2
  82. package/build/components/text-indent-control/index.cjs.map +2 -2
  83. package/build/components/url-input/index.cjs +303 -344
  84. package/build/components/url-input/index.cjs.map +3 -3
  85. package/build/components/writing-flow/index.cjs +2 -0
  86. package/build/components/writing-flow/index.cjs.map +3 -3
  87. package/build/components/writing-flow/use-arrow-nav.cjs +24 -1
  88. package/build/components/writing-flow/use-arrow-nav.cjs.map +2 -2
  89. package/build/components/writing-flow/use-click-selection.cjs +28 -2
  90. package/build/components/writing-flow/use-click-selection.cjs.map +2 -2
  91. package/build/components/writing-flow/use-editable-root-event-handlers.cjs +179 -0
  92. package/build/components/writing-flow/use-editable-root-event-handlers.cjs.map +7 -0
  93. package/build/components/writing-flow/use-editable-root.cjs +5 -42
  94. package/build/components/writing-flow/use-editable-root.cjs.map +2 -2
  95. package/build/components/writing-flow/use-home-end.cjs +1 -1
  96. package/build/components/writing-flow/use-home-end.cjs.map +2 -2
  97. package/build/components/writing-flow/use-selection-observer.cjs +17 -13
  98. package/build/components/writing-flow/use-selection-observer.cjs.map +2 -2
  99. package/build/components/writing-flow/utils.cjs +3 -14
  100. package/build/components/writing-flow/utils.cjs.map +2 -2
  101. package/build/hooks/background.cjs +13 -10
  102. package/build/hooks/background.cjs.map +2 -2
  103. package/build/hooks/block-style-variation.cjs +4 -45
  104. package/build/hooks/block-style-variation.cjs.map +3 -3
  105. package/build/hooks/border.cjs +19 -4
  106. package/build/hooks/border.cjs.map +2 -2
  107. package/build/hooks/custom-css.cjs +6 -2
  108. package/build/hooks/custom-css.cjs.map +2 -2
  109. package/build/hooks/dimensions.cjs +15 -4
  110. package/build/hooks/dimensions.cjs.map +2 -2
  111. package/build/hooks/duotone.cjs +26 -14
  112. package/build/hooks/duotone.cjs.map +3 -3
  113. package/build/hooks/elements.cjs +14 -4
  114. package/build/hooks/elements.cjs.map +2 -2
  115. package/build/hooks/line-height.cjs +1 -2
  116. package/build/hooks/line-height.cjs.map +2 -2
  117. package/build/hooks/typography.cjs +13 -4
  118. package/build/hooks/typography.cjs.map +2 -2
  119. package/build/private-apis.cjs +9 -5
  120. package/build/private-apis.cjs.map +3 -3
  121. package/build/store/actions.cjs +9 -4
  122. package/build/store/actions.cjs.map +2 -2
  123. package/build/store/private-selectors.cjs +28 -1
  124. package/build/store/private-selectors.cjs.map +2 -2
  125. package/build/store/reducer.cjs +6 -6
  126. package/build/store/reducer.cjs.map +2 -2
  127. package/build/store/selectors.cjs +23 -0
  128. package/build/store/selectors.cjs.map +2 -2
  129. package/build/utils/native-undo.cjs +55 -0
  130. package/build/utils/native-undo.cjs.map +7 -0
  131. package/build-module/components/alignment-control/ui.mjs +2 -4
  132. package/build-module/components/alignment-control/ui.mjs.map +2 -2
  133. package/build-module/components/background-image-control/index.mjs +43 -10
  134. package/build-module/components/background-image-control/index.mjs.map +2 -2
  135. package/build-module/components/block-alignment-control/ui.mjs +5 -3
  136. package/build-module/components/block-alignment-control/ui.mjs.map +2 -2
  137. package/build-module/components/block-inspector/index.mjs +74 -46
  138. package/build-module/components/block-inspector/index.mjs.map +2 -2
  139. package/build-module/components/block-list/block-html.mjs +4 -1
  140. package/build-module/components/block-list/block-html.mjs.map +2 -2
  141. package/build-module/components/block-list/use-block-props/index.mjs +5 -2
  142. package/build-module/components/block-list/use-block-props/index.mjs.map +2 -2
  143. package/build-module/components/block-list/use-block-props/use-focus-handler.mjs +2 -3
  144. package/build-module/components/block-list/use-block-props/use-focus-handler.mjs.map +2 -2
  145. package/build-module/components/block-list/use-block-props/use-register-block-event-handlers.mjs +28 -0
  146. package/build-module/components/block-list/use-block-props/use-register-block-event-handlers.mjs.map +7 -0
  147. package/build-module/components/block-parent-selector/index.mjs +2 -2
  148. package/build-module/components/block-parent-selector/index.mjs.map +2 -2
  149. package/build-module/components/block-toolbar/switch-section-style.mjs +4 -2
  150. package/build-module/components/block-toolbar/switch-section-style.mjs.map +2 -2
  151. package/build-module/components/block-tools/empty-block-inserter.mjs +2 -7
  152. package/build-module/components/block-tools/empty-block-inserter.mjs.map +2 -2
  153. package/build-module/components/block-vertical-alignment-control/ui.mjs +4 -6
  154. package/build-module/components/block-vertical-alignment-control/ui.mjs.map +2 -2
  155. package/build-module/components/border-radius-control/utils.mjs +11 -53
  156. package/build-module/components/border-radius-control/utils.mjs.map +2 -2
  157. package/build-module/components/colors-gradients/control.mjs +5 -3
  158. package/build-module/components/colors-gradients/control.mjs.map +2 -2
  159. package/build-module/components/dimension-control/index.mjs +30 -20
  160. package/build-module/components/dimension-control/index.mjs.map +2 -2
  161. package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs +10 -6
  162. package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs.map +2 -2
  163. package/build-module/components/editor-styles/index.mjs +9 -1
  164. package/build-module/components/editor-styles/index.mjs.map +2 -2
  165. package/build-module/components/global-styles/background-panel.mjs +71 -29
  166. package/build-module/components/global-styles/background-panel.mjs.map +2 -2
  167. package/build-module/components/global-styles/border-panel.mjs +157 -56
  168. package/build-module/components/global-styles/border-panel.mjs.map +2 -2
  169. package/build-module/components/global-styles/color-gradient-dropdown-item.mjs +51 -15
  170. package/build-module/components/global-styles/color-gradient-dropdown-item.mjs.map +2 -2
  171. package/build-module/components/global-styles/color-panel.mjs +63 -17
  172. package/build-module/components/global-styles/color-panel.mjs.map +2 -2
  173. package/build-module/components/global-styles/dimensions-panel.mjs +248 -66
  174. package/build-module/components/global-styles/dimensions-panel.mjs.map +2 -2
  175. package/build-module/components/global-styles/filters-panel.mjs +64 -21
  176. package/build-module/components/global-styles/filters-panel.mjs.map +2 -2
  177. package/build-module/components/global-styles/hooks.mjs +4 -1
  178. package/build-module/components/global-styles/hooks.mjs.map +2 -2
  179. package/build-module/components/global-styles/inheritance/index.mjs +108 -0
  180. package/build-module/components/global-styles/inheritance/index.mjs.map +7 -0
  181. package/build-module/components/global-styles/inherited-value-context.mjs +141 -0
  182. package/build-module/components/global-styles/inherited-value-context.mjs.map +7 -0
  183. package/build-module/components/global-styles/shadow-panel-components.mjs +38 -20
  184. package/build-module/components/global-styles/shadow-panel-components.mjs.map +2 -2
  185. package/build-module/components/global-styles/state-control.mjs +1 -1
  186. package/build-module/components/global-styles/state-control.mjs.map +2 -2
  187. package/build-module/components/global-styles/typography-panel.mjs +276 -79
  188. package/build-module/components/global-styles/typography-panel.mjs.map +2 -2
  189. package/build-module/components/inspector-controls-tabs/styles-tab.mjs +6 -1
  190. package/build-module/components/inspector-controls-tabs/styles-tab.mjs.map +2 -2
  191. package/build-module/components/line-height-control/index.mjs +1 -1
  192. package/build-module/components/line-height-control/index.mjs.map +2 -2
  193. package/build-module/components/link-control/index.mjs +4 -2
  194. package/build-module/components/link-control/index.mjs.map +2 -2
  195. package/build-module/components/preset-input-control/index.mjs +2 -1
  196. package/build-module/components/preset-input-control/index.mjs.map +2 -2
  197. package/build-module/components/provider/block-refs-provider.mjs +8 -2
  198. package/build-module/components/provider/block-refs-provider.mjs.map +2 -2
  199. package/build-module/components/provider/index.mjs +1 -1
  200. package/build-module/components/provider/index.mjs.map +2 -2
  201. package/build-module/components/provider/use-block-sync.mjs +18 -6
  202. package/build-module/components/provider/use-block-sync.mjs.map +2 -2
  203. package/build-module/components/provider/use-media-upload-settings.mjs +3 -1
  204. package/build-module/components/provider/use-media-upload-settings.mjs.map +2 -2
  205. package/build-module/components/rich-text/index.mjs +28 -14
  206. package/build-module/components/rich-text/index.mjs.map +2 -2
  207. package/build-module/components/spacing-sizes-control/utils.mjs +6 -18
  208. package/build-module/components/spacing-sizes-control/utils.mjs.map +2 -2
  209. package/build-module/components/text-indent-control/index.mjs +8 -2
  210. package/build-module/components/text-indent-control/index.mjs.map +2 -2
  211. package/build-module/components/url-input/index.mjs +306 -353
  212. package/build-module/components/url-input/index.mjs.map +2 -2
  213. package/build-module/components/writing-flow/index.mjs +2 -0
  214. package/build-module/components/writing-flow/index.mjs.map +2 -2
  215. package/build-module/components/writing-flow/use-arrow-nav.mjs +24 -1
  216. package/build-module/components/writing-flow/use-arrow-nav.mjs.map +2 -2
  217. package/build-module/components/writing-flow/use-click-selection.mjs +28 -2
  218. package/build-module/components/writing-flow/use-click-selection.mjs.map +2 -2
  219. package/build-module/components/writing-flow/use-editable-root-event-handlers.mjs +154 -0
  220. package/build-module/components/writing-flow/use-editable-root-event-handlers.mjs.map +7 -0
  221. package/build-module/components/writing-flow/use-editable-root.mjs +5 -37
  222. package/build-module/components/writing-flow/use-editable-root.mjs.map +2 -2
  223. package/build-module/components/writing-flow/use-home-end.mjs +1 -1
  224. package/build-module/components/writing-flow/use-home-end.mjs.map +2 -2
  225. package/build-module/components/writing-flow/use-selection-observer.mjs +18 -14
  226. package/build-module/components/writing-flow/use-selection-observer.mjs.map +2 -2
  227. package/build-module/components/writing-flow/utils.mjs +2 -11
  228. package/build-module/components/writing-flow/utils.mjs.map +2 -2
  229. package/build-module/hooks/background.mjs +13 -10
  230. package/build-module/hooks/background.mjs.map +2 -2
  231. package/build-module/hooks/block-style-variation.mjs +8 -44
  232. package/build-module/hooks/block-style-variation.mjs.map +2 -2
  233. package/build-module/hooks/border.mjs +19 -4
  234. package/build-module/hooks/border.mjs.map +2 -2
  235. package/build-module/hooks/custom-css.mjs +7 -3
  236. package/build-module/hooks/custom-css.mjs.map +2 -2
  237. package/build-module/hooks/dimensions.mjs +15 -4
  238. package/build-module/hooks/dimensions.mjs.map +2 -2
  239. package/build-module/hooks/duotone.mjs +20 -9
  240. package/build-module/hooks/duotone.mjs.map +2 -2
  241. package/build-module/hooks/elements.mjs +14 -4
  242. package/build-module/hooks/elements.mjs.map +2 -2
  243. package/build-module/hooks/line-height.mjs +1 -2
  244. package/build-module/hooks/line-height.mjs.map +2 -2
  245. package/build-module/hooks/typography.mjs +13 -4
  246. package/build-module/hooks/typography.mjs.map +2 -2
  247. package/build-module/private-apis.mjs +7 -3
  248. package/build-module/private-apis.mjs.map +2 -2
  249. package/build-module/store/actions.mjs +9 -4
  250. package/build-module/store/actions.mjs.map +2 -2
  251. package/build-module/store/private-selectors.mjs +29 -1
  252. package/build-module/store/private-selectors.mjs.map +2 -2
  253. package/build-module/store/reducer.mjs +6 -6
  254. package/build-module/store/reducer.mjs.map +2 -2
  255. package/build-module/store/selectors.mjs +23 -0
  256. package/build-module/store/selectors.mjs.map +2 -2
  257. package/build-module/utils/native-undo.mjs +29 -0
  258. package/build-module/utils/native-undo.mjs.map +7 -0
  259. package/build-style/content-rtl.css +10 -18
  260. package/build-style/content.css +10 -18
  261. package/build-style/style-rtl.css +150 -7
  262. package/build-style/style.css +150 -7
  263. package/package.json +40 -41
  264. package/src/components/alignment-control/test/__snapshots__/index.js.snap +1 -1
  265. package/src/components/alignment-control/ui.js +2 -4
  266. package/src/components/background-image-control/index.js +74 -37
  267. package/src/components/background-image-control/style.scss +13 -4
  268. package/src/components/block-alignment-control/test/__snapshots__/index.js.snap +3 -3
  269. package/src/components/block-alignment-control/test/index.js +1 -1
  270. package/src/components/block-alignment-control/ui.js +4 -2
  271. package/src/components/block-breadcrumb/test/__snapshots__/index.js.snap +1 -1
  272. package/src/components/block-compare/test/__snapshots__/block-view.js.snap +1 -1
  273. package/src/components/block-icon/test/__snapshots__/index.js.snap +1 -1
  274. package/src/components/block-inspector/index.js +60 -31
  275. package/src/components/block-list/block-html.js +5 -0
  276. package/src/components/block-list/content.scss +1 -11
  277. package/src/components/block-list/use-block-props/index.js +5 -1
  278. package/src/components/block-list/use-block-props/use-focus-handler.js +9 -8
  279. package/src/components/block-list/use-block-props/use-register-block-event-handlers.js +45 -0
  280. package/src/components/block-parent-selector/index.js +5 -2
  281. package/src/components/block-toolbar/switch-section-style.js +5 -2
  282. package/src/components/block-tools/empty-block-inserter.js +3 -7
  283. package/src/components/block-vertical-alignment-control/test/__snapshots__/index.js.snap +3 -3
  284. package/src/components/block-vertical-alignment-control/test/index.js +18 -2
  285. package/src/components/block-vertical-alignment-control/ui.js +3 -5
  286. package/src/components/border-radius-control/test/utils.js +0 -41
  287. package/src/components/border-radius-control/utils.js +16 -107
  288. package/src/components/button-block-appender/content.scss +6 -2
  289. package/src/components/color-palette/test/__snapshots__/control.js.snap +1 -1
  290. package/src/components/colors-gradients/control.js +6 -3
  291. package/src/components/default-block-appender/test/__snapshots__/index.js.snap +1 -1
  292. package/src/components/dimension-control/index.js +13 -1
  293. package/src/components/dimensions-tool/aspect-ratio-tool.js +13 -6
  294. package/src/components/editor-styles/index.js +16 -1
  295. package/src/components/global-styles/background-panel.js +107 -34
  296. package/src/components/global-styles/border-panel.js +219 -48
  297. package/src/components/global-styles/color-gradient-dropdown-item.js +93 -28
  298. package/src/components/global-styles/color-panel.js +92 -11
  299. package/src/components/global-styles/dimensions-panel.js +422 -71
  300. package/src/components/global-styles/filters-panel.js +88 -28
  301. package/src/components/global-styles/hooks.js +8 -1
  302. package/src/components/global-styles/inheritance/index.js +204 -0
  303. package/src/components/global-styles/inheritance/style.scss +184 -0
  304. package/src/components/global-styles/inheritance/test/index.js +281 -0
  305. package/src/components/global-styles/inherited-value-context.js +271 -0
  306. package/src/components/global-styles/shadow-panel-components.js +48 -26
  307. package/src/components/global-styles/state-control.js +2 -2
  308. package/src/components/global-styles/test/background-panel.js +338 -1
  309. package/src/components/global-styles/test/border-panel.js +441 -0
  310. package/src/components/global-styles/test/color-panel.js +182 -2
  311. package/src/components/global-styles/test/dimensions-panel.js +753 -0
  312. package/src/components/global-styles/test/filters-panel.js +194 -0
  313. package/src/components/global-styles/test/inherited-value-context-core.js +98 -0
  314. package/src/components/global-styles/test/inherited-value-context.js +454 -0
  315. package/src/components/global-styles/test/typography-panel-core.js +185 -0
  316. package/src/components/global-styles/test/typography-panel.js +849 -12
  317. package/src/components/global-styles/typography-panel.js +449 -100
  318. package/src/components/image-editor/style.scss +33 -0
  319. package/src/components/inner-blocks/test/__snapshots__/index.js.snap +1 -1
  320. package/src/components/inspector-controls-tabs/styles-tab.js +5 -1
  321. package/src/components/line-height-control/index.js +1 -1
  322. package/src/components/link-control/index.js +7 -2
  323. package/src/components/preset-input-control/index.js +4 -1
  324. package/src/components/provider/block-refs-provider.js +12 -2
  325. package/src/components/provider/index.js +2 -2
  326. package/src/components/provider/use-block-sync.js +18 -6
  327. package/src/components/provider/use-media-upload-settings.js +2 -0
  328. package/src/components/rich-text/index.js +37 -16
  329. package/src/components/spacing-sizes-control/test/utils.js +16 -5
  330. package/src/components/spacing-sizes-control/utils.js +11 -28
  331. package/src/components/text-indent-control/index.js +8 -0
  332. package/src/components/url-input/index.js +363 -431
  333. package/src/components/url-input/test/index.js +592 -0
  334. package/src/components/warning/test/__snapshots__/index.js.snap +1 -1
  335. package/src/components/writing-flow/index.js +2 -0
  336. package/src/components/writing-flow/use-arrow-nav.js +51 -1
  337. package/src/components/writing-flow/use-click-selection.js +54 -3
  338. package/src/components/writing-flow/use-editable-root-event-handlers.js +266 -0
  339. package/src/components/writing-flow/use-editable-root.js +7 -77
  340. package/src/components/writing-flow/use-home-end.js +17 -9
  341. package/src/components/writing-flow/use-selection-observer.js +32 -15
  342. package/src/components/writing-flow/utils.js +3 -24
  343. package/src/hooks/background.js +26 -21
  344. package/src/hooks/block-fields/rich-text/styles.scss +1 -1
  345. package/src/hooks/block-style-variation.js +8 -74
  346. package/src/hooks/border.js +19 -4
  347. package/src/hooks/custom-css.js +15 -6
  348. package/src/hooks/dimensions.js +17 -4
  349. package/src/hooks/duotone.js +26 -8
  350. package/src/hooks/elements.js +14 -3
  351. package/src/hooks/line-height.js +0 -1
  352. package/src/hooks/test/inherited-value-wiring.js +411 -0
  353. package/src/hooks/test/preset-round-trip.js +456 -0
  354. package/src/hooks/typography.js +35 -23
  355. package/src/private-apis.js +11 -2
  356. package/src/store/actions.js +10 -4
  357. package/src/store/private-selectors.js +57 -1
  358. package/src/store/reducer.js +9 -9
  359. package/src/store/selectors.js +56 -8
  360. package/src/store/test/private-selectors.js +160 -1
  361. package/src/style.scss +2 -0
  362. package/src/utils/native-undo.js +56 -0
  363. package/build/components/duotone/utils.cjs +0 -87
  364. package/build/components/duotone/utils.cjs.map +0 -7
  365. package/build-module/components/duotone/utils.mjs +0 -59
  366. package/build-module/components/duotone/utils.mjs.map +0 -7
  367. package/src/components/duotone/utils.js +0 -90
  368. package/src/hooks/test/get-variation-styles-with-ref-values.js +0 -91
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Regression coverage for preset round-trip and explicit-empty behavior.
3
+ *
4
+ * The tests verify that supported preset domains decode each serialization
5
+ * form to the same display value, encode resolved overrides back to canonical
6
+ * preset references, clear the relevant reset surfaces, and treat explicit
7
+ * empty values as non-contributing layers.
8
+ */
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import {
14
+ getValueFromVariable,
15
+ getPresetVariableFromValue,
16
+ } from '@wordpress/global-styles-engine';
17
+
18
+ import {
19
+ getCustomValueFromPreset as getCustomSpacingValue,
20
+ getPresetValueFromCustomValue as getSpacingPresetFromCustom,
21
+ isValueSpacingPreset,
22
+ } from '../../components/spacing-sizes-control/utils';
23
+
24
+ import {
25
+ getCustomValueFromPreset,
26
+ getPresetValueFromCustomValue,
27
+ isValuePreset,
28
+ } from '../../components/preset-input-control/utils';
29
+
30
+ // Border-radius equivalents of the spacing helpers above. The per-domain
31
+ // implementations were consolidated into the generic preset-input-control
32
+ // helpers, which take the preset type as an extra argument.
33
+ const BORDER_RADIUS_PRESET_TYPE = 'border-radius';
34
+ const getCustomRadiusValue = ( value, presets ) =>
35
+ getCustomValueFromPreset( value, presets, BORDER_RADIUS_PRESET_TYPE );
36
+ const getRadiusPresetFromCustom = ( value, presets ) =>
37
+ getPresetValueFromCustomValue( value, presets, BORDER_RADIUS_PRESET_TYPE );
38
+ const isValueRadiusPreset = ( value ) =>
39
+ isValuePreset( value, BORDER_RADIUS_PRESET_TYPE );
40
+
41
+ // Fixture: a minimal merged GlobalStylesConfig sized just large enough
42
+ // to exercise every (form × domain) cell. Mirrors the two-branch shape
43
+ // real Global Styles payloads use ({ settings, styles }).
44
+ //
45
+ // Note on preset shape: `findInPresetsBy()` (engine-internal) iterates
46
+ // presets by origin in priority order [ 'custom', 'theme', 'default' ].
47
+ // A flat array does NOT match. Real GS payloads are origin-keyed; the
48
+ // fixture mirrors that exactly. The per-domain block-editor helpers
49
+ // (spacing-sizes-control, preset-input-control) take the FLAT preset
50
+ // list, so we keep both shapes (RAW_PRESETS for the helpers,
51
+ // origin-keyed wrappers for the engine).
52
+ const RAW_PRESETS = {
53
+ color: [
54
+ { slug: 'vivid-red', color: '#cf2e2e', name: 'Vivid red' },
55
+ { slug: 'pale-blue', color: '#a5e3ff', name: 'Pale blue' },
56
+ ],
57
+ fontSizes: [
58
+ { slug: 'large', size: '24px', name: 'Large' },
59
+ { slug: 'small', size: '14px', name: 'Small' },
60
+ ],
61
+ spacingSizes: [
62
+ { slug: '40', size: '32px', name: 'Large' },
63
+ { slug: '20', size: '8px', name: 'Small' },
64
+ ],
65
+ radiusSizes: [
66
+ { slug: 'rounded', size: '8px', name: 'Rounded' },
67
+ { slug: 'pill', size: '999px', name: 'Pill' },
68
+ ],
69
+ };
70
+
71
+ const FEATURES = {
72
+ settings: {
73
+ color: {
74
+ palette: { theme: RAW_PRESETS.color },
75
+ },
76
+ typography: {
77
+ fontSizes: { theme: RAW_PRESETS.fontSizes },
78
+ },
79
+ spacing: {
80
+ spacingSizes: { theme: RAW_PRESETS.spacingSizes },
81
+ },
82
+ border: {
83
+ radiusSizes: { theme: RAW_PRESETS.radiusSizes },
84
+ },
85
+ },
86
+ styles: {
87
+ // Targets for the `{ ref }` decode leg.
88
+ color: {
89
+ text: '#cf2e2e',
90
+ background: '#a5e3ff',
91
+ },
92
+ typography: {
93
+ fontSize: '24px',
94
+ },
95
+ spacing: {
96
+ padding: { top: '32px' },
97
+ },
98
+ border: {
99
+ radius: '8px',
100
+ },
101
+ },
102
+ };
103
+
104
+ const PRESETS = {
105
+ spacing: RAW_PRESETS.spacingSizes,
106
+ 'border-radius': RAW_PRESETS.radiusSizes,
107
+ };
108
+
109
+ /**
110
+ * Simulate the panel's "reset all" path against a block's attributes.
111
+ * Returns a new attributes object with every reset surface cleared for
112
+ * the given style path. The reset surfaces are:
113
+ *
114
+ * 1. The per-domain slug attribute (e.g. attributes.textColor, .fontSize)
115
+ * 2. The serialized style-path var (e.g. attributes.style.color.text)
116
+ *
117
+ * The auto-generated CSS class (e.g. `has-vivid-red-color`) is derived
118
+ * from (1) at save() time, so clearing (1) clears the class downstream —
119
+ * verified indirectly by asserting (1) is undefined after reset.
120
+ *
121
+ * @param {Object} attributes Block attributes object.
122
+ * @param {?string} slugAttrName Per-domain slug attribute key, or null.
123
+ * @param {string} stylePath Dotted style sub-path to clear.
124
+ * @return {Object} New attributes with the reset surfaces cleared.
125
+ */
126
+ function simulateResetAll( attributes, slugAttrName, stylePath ) {
127
+ const next = { ...attributes };
128
+ if ( slugAttrName ) {
129
+ next[ slugAttrName ] = undefined;
130
+ }
131
+ if ( stylePath ) {
132
+ const segments = stylePath.split( '.' );
133
+ next.style = JSON.parse( JSON.stringify( next.style || {} ) );
134
+ let node = next.style;
135
+ for ( let i = 0; i < segments.length - 1; i++ ) {
136
+ if ( ! node[ segments[ i ] ] ) {
137
+ return next;
138
+ }
139
+ node = node[ segments[ i ] ];
140
+ }
141
+ node[ segments[ segments.length - 1 ] ] = undefined;
142
+ }
143
+ return next;
144
+ }
145
+
146
+ // ---------------------------------------------------------------------------
147
+ // COLOR — has STYLE_PATH_TO_CSS_VAR_INFIX entry → uses GS engine helpers.
148
+ // ---------------------------------------------------------------------------
149
+ describe( 'color preset round-trip', () => {
150
+ const stylePath = 'color.text';
151
+ const slugAttr = 'textColor';
152
+ const expectedResolved = '#cf2e2e';
153
+ const expectedPresetVar = 'var:preset|color|vivid-red';
154
+
155
+ it.each( [
156
+ [ 'var:preset|<infix>|<slug>', 'var:preset|color|vivid-red' ],
157
+ [
158
+ 'var(--wp--preset--<infix>--<slug>)',
159
+ 'var(--wp--preset--color--vivid-red)',
160
+ ],
161
+ [ 'raw resolved', '#cf2e2e' ],
162
+ [ '{ ref }', { ref: 'styles.color.text' } ],
163
+ ] )( 'decodes [%s] for display', ( _label, input ) => {
164
+ const decoded = getValueFromVariable( FEATURES, undefined, input );
165
+ expect( decoded ).toBe( expectedResolved );
166
+ } );
167
+
168
+ it( 'encodes a resolved override back to var:preset|color|<slug>', () => {
169
+ const encoded = getPresetVariableFromValue(
170
+ FEATURES.settings,
171
+ undefined,
172
+ stylePath,
173
+ expectedResolved
174
+ );
175
+ expect( encoded ).toBe( expectedPresetVar );
176
+ } );
177
+
178
+ it( 'encodes a non-preset color through unchanged (custom value)', () => {
179
+ const encoded = getPresetVariableFromValue(
180
+ FEATURES.settings,
181
+ undefined,
182
+ stylePath,
183
+ '#000000'
184
+ );
185
+ expect( encoded ).toBe( '#000000' );
186
+ } );
187
+
188
+ it( 'full-reset clears slug attribute + style path', () => {
189
+ const before = {
190
+ textColor: 'vivid-red',
191
+ style: { color: { text: 'var:preset|color|vivid-red' } },
192
+ };
193
+ const after = simulateResetAll( before, slugAttr, stylePath );
194
+ expect( after.textColor ).toBeUndefined();
195
+ expect( after.style.color.text ).toBeUndefined();
196
+ } );
197
+ } );
198
+
199
+ // ---------------------------------------------------------------------------
200
+ // FONT-SIZE — has STYLE_PATH_TO_CSS_VAR_INFIX entry; valueFunc may run
201
+ // getTypographyFontSizeValue but here our preset already has a plain `size`.
202
+ // ---------------------------------------------------------------------------
203
+ describe( 'font-size preset round-trip', () => {
204
+ const stylePath = 'typography.fontSize';
205
+ const slugAttr = 'fontSize';
206
+ const expectedResolved = '24px';
207
+ const expectedPresetVar = 'var:preset|font-size|large';
208
+
209
+ it.each( [
210
+ [ 'var:preset|<infix>|<slug>', 'var:preset|font-size|large' ],
211
+ [
212
+ 'var(--wp--preset--<infix>--<slug>)',
213
+ 'var(--wp--preset--font-size--large)',
214
+ ],
215
+ [ 'raw resolved', '24px' ],
216
+ [ '{ ref }', { ref: 'styles.typography.fontSize' } ],
217
+ ] )( 'decodes [%s] for display', ( _label, input ) => {
218
+ const decoded = getValueFromVariable( FEATURES, undefined, input );
219
+ expect( decoded ).toBe( expectedResolved );
220
+ } );
221
+
222
+ it( 'encodes a resolved override back to var:preset|font-size|<slug>', () => {
223
+ const encoded = getPresetVariableFromValue(
224
+ FEATURES.settings,
225
+ undefined,
226
+ stylePath,
227
+ expectedResolved
228
+ );
229
+ expect( encoded ).toBe( expectedPresetVar );
230
+ } );
231
+
232
+ it( 'encodes a non-preset size through unchanged (custom value)', () => {
233
+ const encoded = getPresetVariableFromValue(
234
+ FEATURES.settings,
235
+ undefined,
236
+ stylePath,
237
+ '19px'
238
+ );
239
+ expect( encoded ).toBe( '19px' );
240
+ } );
241
+
242
+ it( 'full-reset clears slug attribute + style path', () => {
243
+ const before = {
244
+ fontSize: 'large',
245
+ style: { typography: { fontSize: 'var:preset|font-size|large' } },
246
+ };
247
+ const after = simulateResetAll( before, slugAttr, stylePath );
248
+ expect( after.fontSize ).toBeUndefined();
249
+ expect( after.style.typography.fontSize ).toBeUndefined();
250
+ } );
251
+ } );
252
+
253
+ // ---------------------------------------------------------------------------
254
+ // SPACING — NOT in STYLE_PATH_TO_CSS_VAR_INFIX. Uses the per-domain
255
+ // helpers in spacing-sizes-control/utils.js, keyed against spacingSizes
256
+ // presets. No slug attribute exists; spacing lives only in
257
+ // style.spacing.*.
258
+ // ---------------------------------------------------------------------------
259
+ describe( 'spacing preset round-trip', () => {
260
+ const stylePath = 'spacing.padding.top';
261
+ const slugAttr = null; // no slug-attribute leg for spacing
262
+ const expectedResolved = '32px';
263
+ const expectedPresetVar = 'var:preset|spacing|40';
264
+ const presets = PRESETS.spacing;
265
+
266
+ // Decode leg.
267
+ it( 'decodes var:preset|spacing|<slug> via getCustomValueFromPreset', () => {
268
+ expect( getCustomSpacingValue( expectedPresetVar, presets ) ).toBe(
269
+ expectedResolved
270
+ );
271
+ } );
272
+
273
+ it( 'decodes var(--wp--preset--spacing--<slug>) via getValueFromVariable', () => {
274
+ // The block-editor spacing helper is regex-based and only
275
+ // matches `var:preset|…` form. The engine's
276
+ // `getValueFromVariable` handles the CSS-custom-property form
277
+ // via the same PRESET_METADATA. Both paths must reach the same
278
+ // display value or the panel has to choose one.
279
+ const decoded = getValueFromVariable(
280
+ FEATURES,
281
+ undefined,
282
+ 'var(--wp--preset--spacing--40)'
283
+ );
284
+ expect( decoded ).toBe( expectedResolved );
285
+ } );
286
+
287
+ it( 'decodes raw resolved value as-is (passthrough)', () => {
288
+ expect( getCustomSpacingValue( '32px', presets ) ).toBe( '32px' );
289
+ } );
290
+
291
+ it( 'decodes { ref: … } via getValueFromVariable', () => {
292
+ const decoded = getValueFromVariable( FEATURES, undefined, {
293
+ ref: 'styles.spacing.padding.top',
294
+ } );
295
+ expect( decoded ).toBe( expectedResolved );
296
+ } );
297
+
298
+ // Encode leg.
299
+ it( 'encodes a resolved override back to var:preset|spacing|<slug>', () => {
300
+ expect( getSpacingPresetFromCustom( expectedResolved, presets ) ).toBe(
301
+ expectedPresetVar
302
+ );
303
+ } );
304
+
305
+ it( 'encodes a non-preset value through unchanged (custom value)', () => {
306
+ expect( getSpacingPresetFromCustom( '17px', presets ) ).toBe( '17px' );
307
+ } );
308
+
309
+ // Reset leg.
310
+ it( 'full-reset clears style.spacing.padding.top (no slug attribute)', () => {
311
+ const before = {
312
+ style: { spacing: { padding: { top: 'var:preset|spacing|40' } } },
313
+ };
314
+ const after = simulateResetAll( before, slugAttr, stylePath );
315
+ expect( after.style.spacing.padding.top ).toBeUndefined();
316
+ } );
317
+
318
+ it( 'isValueSpacingPreset recognises the canonical form', () => {
319
+ expect( isValueSpacingPreset( expectedPresetVar ) ).toBe( true );
320
+ expect( isValueSpacingPreset( '32px' ) ).toBe( false );
321
+ } );
322
+ } );
323
+
324
+ // ---------------------------------------------------------------------------
325
+ // BORDER-RADIUS — NOT in STYLE_PATH_TO_CSS_VAR_INFIX. Uses the generic
326
+ // helpers in preset-input-control/utils.js with the 'border-radius' preset
327
+ // type. No slug attribute exists; border-radius lives only in
328
+ // style.border.radius.
329
+ // ---------------------------------------------------------------------------
330
+ describe( 'border-radius preset round-trip', () => {
331
+ const stylePath = 'border.radius';
332
+ const slugAttr = null;
333
+ const expectedResolved = '8px';
334
+ const expectedPresetVar = 'var:preset|border-radius|rounded';
335
+ const presets = PRESETS[ 'border-radius' ];
336
+
337
+ // Decode leg.
338
+ it( 'decodes var:preset|border-radius|<slug> via getCustomValueFromPreset', () => {
339
+ expect( getCustomRadiusValue( expectedPresetVar, presets ) ).toBe(
340
+ expectedResolved
341
+ );
342
+ } );
343
+
344
+ it( 'decodes var(--wp--preset--border-radius--<slug>) via getValueFromVariable', () => {
345
+ const decoded = getValueFromVariable(
346
+ FEATURES,
347
+ undefined,
348
+ 'var(--wp--preset--border-radius--rounded)'
349
+ );
350
+ expect( decoded ).toBe( expectedResolved );
351
+ } );
352
+
353
+ it( 'decodes raw resolved value as-is (passthrough)', () => {
354
+ expect( getCustomRadiusValue( '8px', presets ) ).toBe( '8px' );
355
+ } );
356
+
357
+ it( 'decodes { ref: … } via getValueFromVariable', () => {
358
+ const decoded = getValueFromVariable( FEATURES, undefined, {
359
+ ref: 'styles.border.radius',
360
+ } );
361
+ expect( decoded ).toBe( expectedResolved );
362
+ } );
363
+
364
+ // Encode leg.
365
+ it( 'encodes a resolved override back to var:preset|border-radius|<slug>', () => {
366
+ expect( getRadiusPresetFromCustom( expectedResolved, presets ) ).toBe(
367
+ expectedPresetVar
368
+ );
369
+ } );
370
+
371
+ it( 'encodes a non-preset value through unchanged (custom value)', () => {
372
+ expect( getRadiusPresetFromCustom( '12px', presets ) ).toBe( '12px' );
373
+ } );
374
+
375
+ // Reset leg.
376
+ it( 'full-reset clears style.border.radius (no slug attribute)', () => {
377
+ const before = {
378
+ style: {
379
+ border: { radius: 'var:preset|border-radius|rounded' },
380
+ },
381
+ };
382
+ const after = simulateResetAll( before, slugAttr, stylePath );
383
+ expect( after.style.border.radius ).toBeUndefined();
384
+ } );
385
+
386
+ it( 'isValuePreset recognises the canonical form', () => {
387
+ expect( isValueRadiusPreset( expectedPresetVar ) ).toBe( true );
388
+ expect( isValueRadiusPreset( '8px' ) ).toBe( false );
389
+ } );
390
+ } );
391
+
392
+ // ---------------------------------------------------------------------------
393
+ // Explicit-empty handling per domain.
394
+ //
395
+ // Empty leaves must be treated as non-contributing layers so controls can
396
+ // fall back to the next layer instead of rendering an empty placeholder.
397
+ // ---------------------------------------------------------------------------
398
+ describe( 'explicit-empty handling per domain', () => {
399
+ // Reproduce `resolveStyle`'s emptiness predicate verbatim.
400
+ function isEmpty( v ) {
401
+ if ( v === '' || v === null || v === undefined ) {
402
+ return true;
403
+ }
404
+ if (
405
+ typeof v === 'object' &&
406
+ ! Array.isArray( v ) &&
407
+ Object.keys( v ).length === 0
408
+ ) {
409
+ return true;
410
+ }
411
+ return false;
412
+ }
413
+
414
+ it.each( [
415
+ [ 'color (text)', '' ],
416
+ [ 'color (text)', null ],
417
+ [ 'color (text)', {} ],
418
+ [ 'font-size', '' ],
419
+ [ 'font-size', null ],
420
+ [ 'font-size', {} ],
421
+ [ 'spacing (padding.top)', '' ],
422
+ [ 'spacing (padding.top)', null ],
423
+ [ 'spacing (padding.top)', {} ],
424
+ [ 'border-radius', '' ],
425
+ [ 'border-radius', null ],
426
+ [ 'border-radius', {} ],
427
+ ] )(
428
+ '%s with value %p is recognised as a non-contributing layer',
429
+ ( _domain, value ) => {
430
+ expect( isEmpty( value ) ).toBe( true );
431
+ }
432
+ );
433
+
434
+ // Sanity: zero-valued (but meaningful) leaves are NOT empty. A
435
+ // spacing of '0' or a border radius of '0' is a real, contributing
436
+ // value.
437
+ it.each( [
438
+ [ 'color', '#000000' ],
439
+ [ 'font-size', '0px' ],
440
+ [ 'spacing', '0' ],
441
+ [ 'border-radius', '0' ],
442
+ ] )(
443
+ '%s with concrete value %p is NOT an empty layer',
444
+ ( _domain, value ) => {
445
+ expect( isEmpty( value ) ).toBe( false );
446
+ }
447
+ );
448
+
449
+ // Cross-check against the engine's `getValueFromVariable`: an
450
+ // empty-string input round-trips to itself (the resolver's job is
451
+ // to drop the layer; the engine's job is just to leave non-variables
452
+ // alone).
453
+ it( 'getValueFromVariable returns empty string unchanged (resolver drops the layer downstream)', () => {
454
+ expect( getValueFromVariable( FEATURES, undefined, '' ) ).toBe( '' );
455
+ } );
456
+ } );
@@ -14,6 +14,7 @@ import {
14
14
  default as StylesTypographyPanel,
15
15
  useHasTypographyPanel,
16
16
  } from '../components/global-styles/typography-panel';
17
+ import { useResolvedStyle } from '../components/global-styles/inherited-value-context';
17
18
 
18
19
  import { LINE_HEIGHT_SUPPORT_KEY } from './line-height';
19
20
  import { FONT_FAMILY_SUPPORT_KEY } from './font-family';
@@ -151,32 +152,42 @@ export function TypographyPanel( {
151
152
  const selectedState = useBlockStyleState();
152
153
  const isEnabled = useHasTypographyPanel( settings );
153
154
 
154
- const { style, fontFamily, fontSize, fitText, textColor } = useSelect(
155
- ( select ) => {
156
- // Early return to avoid subscription when disabled.
157
- if ( ! isEnabled ) {
158
- return {};
159
- }
160
- const {
161
- style: _style,
162
- fontFamily: _fontFamily,
163
- fontSize: _fontSize,
164
- fitText: _fitText,
165
- textColor: _textColor,
166
- } = select( blockEditorStore ).getBlockAttributes( clientId ) || {};
167
- return {
168
- style: _style,
169
- fontFamily: _fontFamily,
170
- fontSize: _fontSize,
171
- fitText: _fitText,
172
- textColor: _textColor,
173
- };
174
- },
175
- [ clientId, isEnabled ]
176
- );
155
+ const { style, fontFamily, fontSize, fitText, textColor, className } =
156
+ useSelect(
157
+ ( select ) => {
158
+ // Early return to avoid subscription when disabled.
159
+ if ( ! isEnabled ) {
160
+ return {};
161
+ }
162
+ const {
163
+ style: _style,
164
+ fontFamily: _fontFamily,
165
+ fontSize: _fontSize,
166
+ fitText: _fitText,
167
+ textColor: _textColor,
168
+ className: _className,
169
+ } = select( blockEditorStore ).getBlockAttributes( clientId ) ||
170
+ {};
171
+ return {
172
+ style: _style,
173
+ fontFamily: _fontFamily,
174
+ fontSize: _fontSize,
175
+ fitText: _fitText,
176
+ textColor: _textColor,
177
+ className: _className,
178
+ };
179
+ },
180
+ [ clientId, isEnabled ]
181
+ );
177
182
 
178
183
  const isStateSelected = ! isDefaultBlockStyleState( selectedState );
179
184
 
185
+ const { value: inheritedValue } = useResolvedStyle(
186
+ name,
187
+ className,
188
+ selectedState
189
+ );
190
+
180
191
  const value = useMemo( () => {
181
192
  if ( isStateSelected ) {
182
193
  return getStyleForState( style, selectedState );
@@ -257,6 +268,7 @@ export function TypographyPanel( {
257
268
  onChange={ onChange }
258
269
  defaultControls={ defaultControls }
259
270
  contrastWarning={ contrastWarning }
271
+ inheritedValue={ inheritedValue }
260
272
  />
261
273
  );
262
274
  }
@@ -1,9 +1,14 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { privateApis as globalStylesEnginePrivateApis } from '@wordpress/global-styles-engine';
5
+
1
6
  /**
2
7
  * Internal dependencies
3
8
  */
4
9
  import * as globalStyles from './components/global-styles';
5
10
  import { ExperimentalBlockEditorProvider } from './components/provider';
6
- import { lock } from './lock-unlock';
11
+ import { lock, unlock } from './lock-unlock';
7
12
  import { getRichTextValues } from './components/rich-text/get-rich-text-values';
8
13
  import ResizableBoxPopover from './components/resizable-box-popover';
9
14
  import { default as PrivateQuickInserter } from './components/inserter/quick-inserter';
@@ -40,7 +45,6 @@ import {
40
45
  ExperimentalBlockCanvas,
41
46
  BlockCanvasCover,
42
47
  } from './components/block-canvas';
43
- import { getDuotoneFilter } from './components/duotone/utils';
44
48
  import { useFlashEditableBlocks } from './components/use-flash-editable-blocks';
45
49
  import {
46
50
  selectBlockPatternsKey,
@@ -79,11 +83,14 @@ import useRemoteUrlData from './components/link-control/use-rich-url-data';
79
83
  import { PrivateBlockContext } from './components/block-list/private-block-context';
80
84
  import useListViewPanelState from './components/use-list-view-panel-state';
81
85
  import InnerContent from './components/inner-content';
86
+ import { useNativeUndo, usesNativeUndo } from './utils/native-undo';
82
87
  import {
83
88
  isHashLink,
84
89
  isRelativePath,
85
90
  } from './components/link-control/is-url-like';
86
91
 
92
+ const { getDuotoneFilter } = unlock( globalStylesEnginePrivateApis );
93
+
87
94
  /**
88
95
  * Private @wordpress/block-editor APIs.
89
96
  */
@@ -157,4 +164,6 @@ lock( privateApis, {
157
164
  isHashLink,
158
165
  isRelativePath,
159
166
  InnerContent,
167
+ useNativeUndo,
168
+ usesNativeUndo,
160
169
  } );
@@ -1253,10 +1253,16 @@ export const mergeBlocks =
1253
1253
  }
1254
1254
 
1255
1255
  if ( isUnmodifiedDefaultBlock( blockA ) ) {
1256
- dispatch.removeBlock(
1257
- clientIdA,
1258
- select.isBlockSelected( clientIdA )
1259
- );
1256
+ const isASelected = select.isBlockSelected( clientIdA );
1257
+
1258
+ if ( isASelected ) {
1259
+ registry.batch( () => {
1260
+ dispatch.removeBlock( clientIdA, false );
1261
+ dispatch.selectBlock( clientIdB, 0 );
1262
+ } );
1263
+ } else {
1264
+ dispatch.removeBlock( clientIdA, false );
1265
+ }
1260
1266
  return;
1261
1267
  }
1262
1268
 
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { createSelector, createRegistrySelector } from '@wordpress/data';
5
5
  import {
6
+ getBlockType,
6
7
  hasBlockSupport,
7
8
  privateApis as blocksPrivateApis,
8
9
  } from '@wordpress/blocks';
@@ -15,6 +16,7 @@ import {
15
16
  getBlockOrder,
16
17
  getBlockParents,
17
18
  getBlockEditingMode,
19
+ getBlockMode,
18
20
  getBlockListSettings,
19
21
  getSettings,
20
22
  canInsertBlockType,
@@ -42,7 +44,7 @@ import {
42
44
  } from './private-keys';
43
45
  import { BLOCK_VISIBILITY_VIEWPORTS } from '../components/block-visibility/constants';
44
46
 
45
- const { isContentBlock } = unlock( blocksPrivateApis );
47
+ const { isContentBlock, editableRootKey } = unlock( blocksPrivateApis );
46
48
  const { getViewportBreakpoints } = unlock( globalStylesEnginePrivateApis );
47
49
 
48
50
  export { getBlockSettings } from './get-block-settings';
@@ -243,6 +245,60 @@ function hasExplicitDisabledParent( state, clientId ) {
243
245
  return false;
244
246
  }
245
247
 
248
+ /**
249
+ * Returns true when the writing flow wrapper can host editing for the given
250
+ * block: it supports `editableRoot`, is edited visually in the default
251
+ * editing mode, and has sibling blocks for a native selection to extend
252
+ * into, all of them editable.
253
+ *
254
+ * @param {Object} state Global application state.
255
+ * @param {string} clientId Block client ID.
256
+ *
257
+ * @return {boolean} Whether an editing host can host the block.
258
+ */
259
+ export const canHostEditableRoot = createSelector(
260
+ ( state, clientId ) => {
261
+ if (
262
+ ! clientId ||
263
+ getBlockEditingMode( state, clientId ) !== 'default' ||
264
+ // Not when the block is edited as HTML: there is no rich text to
265
+ // host then, only a textarea, which the editing host would
266
+ // interfere with.
267
+ getBlockMode( state, clientId ) !== 'visual' ||
268
+ ! getBlockType( getBlockName( state, clientId ) )?.[
269
+ editableRootKey
270
+ ]
271
+ ) {
272
+ return false;
273
+ }
274
+
275
+ // Only host when the block has sibling blocks for a native selection to
276
+ // extend into, all of them editable. A lone block (e.g. a single
277
+ // paragraph nested in an HTML block) is edited on its own element, and
278
+ // read-only siblings (e.g. pattern content without overrides enabled)
279
+ // must not become editable by inheriting from the host.
280
+ const siblings = getBlockOrder(
281
+ state,
282
+ getBlockRootClientId( state, clientId )
283
+ );
284
+ return (
285
+ siblings.length > 1 &&
286
+ siblings.every(
287
+ ( siblingClientId ) =>
288
+ getBlockEditingMode( state, siblingClientId ) === 'default'
289
+ )
290
+ );
291
+ },
292
+ ( state ) => [
293
+ state.blocks.order,
294
+ state.blocks.parents,
295
+ state.blocks.byClientId,
296
+ state.blocks.blockEditingModes,
297
+ state.derivedBlockEditingModes,
298
+ state.blocksMode,
299
+ ]
300
+ );
301
+
246
302
  /**
247
303
  * Returns the block tree displayed by List View.
248
304
  *