@wordpress/block-editor 15.8.1-next.16d95556a.0 → 15.9.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 (302) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-inspector/edit-contents.js +19 -23
  3. package/build/components/block-inspector/edit-contents.js.map +3 -3
  4. package/build/components/block-inspector/index.js +7 -1
  5. package/build/components/block-inspector/index.js.map +2 -2
  6. package/build/components/block-list/block.js +4 -0
  7. package/build/components/block-list/block.js.map +2 -2
  8. package/build/components/block-list/index.js +2 -1
  9. package/build/components/block-list/index.js.map +2 -2
  10. package/build/components/block-list/use-block-props/index.js +3 -1
  11. package/build/components/block-list/use-block-props/index.js.map +2 -2
  12. package/build/components/block-list/use-block-props/use-is-hovered.js +16 -10
  13. package/build/components/block-list/use-block-props/use-is-hovered.js.map +2 -2
  14. package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js +27 -5
  15. package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +2 -2
  16. package/build/components/block-lock/modal.js +5 -5
  17. package/build/components/block-lock/modal.js.map +2 -2
  18. package/build/components/block-lock/use-block-lock.js +10 -13
  19. package/build/components/block-lock/use-block-lock.js.map +2 -2
  20. package/build/components/block-settings-menu-controls/edit-section-menu-item.js +64 -0
  21. package/build/components/block-settings-menu-controls/edit-section-menu-item.js.map +7 -0
  22. package/build/components/block-settings-menu-controls/index.js +9 -1
  23. package/build/components/block-settings-menu-controls/index.js.map +2 -2
  24. package/build/components/block-toolbar/block-toolbar-icon.js +9 -9
  25. package/build/components/block-toolbar/block-toolbar-icon.js.map +2 -2
  26. package/build/components/block-tools/index.js +56 -45
  27. package/build/components/block-tools/index.js.map +3 -3
  28. package/build/components/block-variation-transforms/index.js +32 -5
  29. package/build/components/block-variation-transforms/index.js.map +2 -2
  30. package/build/components/block-visibility/toolbar.js +1 -1
  31. package/build/components/block-visibility/toolbar.js.map +1 -1
  32. package/build/components/border-radius-control/single-input-control.js +1 -0
  33. package/build/components/border-radius-control/single-input-control.js.map +2 -2
  34. package/build/components/content-only-controls/fields-dropdown-menu.js +66 -0
  35. package/build/components/content-only-controls/fields-dropdown-menu.js.map +7 -0
  36. package/build/components/content-only-controls/index.js +444 -0
  37. package/build/components/content-only-controls/index.js.map +7 -0
  38. package/build/components/content-only-controls/link/index.js +193 -0
  39. package/build/components/content-only-controls/link/index.js.map +7 -0
  40. package/build/components/content-only-controls/media/index.js +264 -0
  41. package/build/components/content-only-controls/media/index.js.map +7 -0
  42. package/build/components/content-only-controls/rich-text/index.js +188 -0
  43. package/build/components/content-only-controls/rich-text/index.js.map +7 -0
  44. package/build/components/content-only-controls/use-inspector-popover-placement.js +41 -0
  45. package/build/components/content-only-controls/use-inspector-popover-placement.js.map +7 -0
  46. package/build/components/font-family/index.js +1 -15
  47. package/build/components/font-family/index.js.map +2 -2
  48. package/build/components/global-styles/dimensions-panel.js +35 -2
  49. package/build/components/global-styles/dimensions-panel.js.map +2 -2
  50. package/build/components/global-styles/hooks.js +1 -1
  51. package/build/components/global-styles/hooks.js.map +2 -2
  52. package/build/components/global-styles/typography-panel.js +1 -2
  53. package/build/components/global-styles/typography-panel.js.map +2 -2
  54. package/build/components/inserter/media-tab/media-tab.js +1 -33
  55. package/build/components/inserter/media-tab/media-tab.js.map +3 -3
  56. package/build/components/inspector-controls-tabs/content-tab.js +6 -2
  57. package/build/components/inspector-controls-tabs/content-tab.js.map +3 -3
  58. package/build/components/inspector-controls-tabs/index.js +7 -1
  59. package/build/components/inspector-controls-tabs/index.js.map +2 -2
  60. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.js +1 -1
  61. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.js.map +2 -2
  62. package/build/components/link-control/index.js +15 -7
  63. package/build/components/link-control/index.js.map +2 -2
  64. package/build/components/list-view/block-select-button.js +7 -8
  65. package/build/components/list-view/block-select-button.js.map +2 -2
  66. package/build/components/list-view/block.js +9 -7
  67. package/build/components/list-view/block.js.map +2 -2
  68. package/build/components/media-placeholder/index.js +18 -35
  69. package/build/components/media-placeholder/index.js.map +3 -3
  70. package/build/components/media-placeholder/utils.js +60 -0
  71. package/build/components/media-placeholder/utils.js.map +7 -0
  72. package/build/components/media-replace-flow/index.js +24 -33
  73. package/build/components/media-replace-flow/index.js.map +3 -3
  74. package/build/components/use-block-commands/index.js +1 -1
  75. package/build/components/use-block-commands/index.js.map +2 -2
  76. package/build/components/use-block-display-information/index.js +21 -1
  77. package/build/components/use-block-display-information/index.js.map +3 -3
  78. package/build/components/use-block-drop-zone/index.js +1 -5
  79. package/build/components/use-block-drop-zone/index.js.map +2 -2
  80. package/build/hooks/block-bindings.js +52 -61
  81. package/build/hooks/block-bindings.js.map +3 -3
  82. package/build/hooks/dimensions.js +3 -3
  83. package/build/hooks/dimensions.js.map +2 -2
  84. package/build/hooks/metadata.js +1 -1
  85. package/build/hooks/metadata.js.map +2 -2
  86. package/build/hooks/use-content-only-section-edit.js +67 -0
  87. package/build/hooks/use-content-only-section-edit.js.map +7 -0
  88. package/build/hooks/utils.js +5 -1
  89. package/build/hooks/utils.js.map +2 -2
  90. package/build/layouts/constrained.js +2 -2
  91. package/build/layouts/constrained.js.map +2 -2
  92. package/build/private-apis.js +2 -3
  93. package/build/private-apis.js.map +3 -3
  94. package/build/store/private-keys.js +3 -0
  95. package/build/store/private-keys.js.map +2 -2
  96. package/build/store/private-selectors.js +41 -2
  97. package/build/store/private-selectors.js.map +2 -2
  98. package/build/store/selectors.js +6 -4
  99. package/build/store/selectors.js.map +2 -2
  100. package/build/utils/fit-text-utils.js +9 -1
  101. package/build/utils/fit-text-utils.js.map +2 -2
  102. package/build-module/components/block-inspector/edit-contents.js +9 -23
  103. package/build-module/components/block-inspector/edit-contents.js.map +2 -2
  104. package/build-module/components/block-inspector/index.js +7 -1
  105. package/build-module/components/block-inspector/index.js.map +2 -2
  106. package/build-module/components/block-list/block.js +4 -0
  107. package/build-module/components/block-list/block.js.map +2 -2
  108. package/build-module/components/block-list/index.js +2 -1
  109. package/build-module/components/block-list/index.js.map +2 -2
  110. package/build-module/components/block-list/use-block-props/index.js +3 -1
  111. package/build-module/components/block-list/use-block-props/index.js.map +2 -2
  112. package/build-module/components/block-list/use-block-props/use-is-hovered.js +16 -10
  113. package/build-module/components/block-list/use-block-props/use-is-hovered.js.map +2 -2
  114. package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js +27 -5
  115. package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +2 -2
  116. package/build-module/components/block-lock/modal.js +5 -5
  117. package/build-module/components/block-lock/modal.js.map +2 -2
  118. package/build-module/components/block-lock/use-block-lock.js +10 -13
  119. package/build-module/components/block-lock/use-block-lock.js.map +2 -2
  120. package/build-module/components/block-settings-menu-controls/edit-section-menu-item.js +29 -0
  121. package/build-module/components/block-settings-menu-controls/edit-section-menu-item.js.map +7 -0
  122. package/build-module/components/block-settings-menu-controls/index.js +9 -1
  123. package/build-module/components/block-settings-menu-controls/index.js.map +2 -2
  124. package/build-module/components/block-toolbar/block-toolbar-icon.js +10 -10
  125. package/build-module/components/block-toolbar/block-toolbar-icon.js.map +2 -2
  126. package/build-module/components/block-tools/index.js +56 -45
  127. package/build-module/components/block-tools/index.js.map +2 -2
  128. package/build-module/components/block-variation-transforms/index.js +32 -5
  129. package/build-module/components/block-variation-transforms/index.js.map +2 -2
  130. package/build-module/components/block-visibility/toolbar.js +1 -1
  131. package/build-module/components/block-visibility/toolbar.js.map +1 -1
  132. package/build-module/components/border-radius-control/single-input-control.js +1 -0
  133. package/build-module/components/border-radius-control/single-input-control.js.map +2 -2
  134. package/build-module/components/content-only-controls/fields-dropdown-menu.js +45 -0
  135. package/build-module/components/content-only-controls/fields-dropdown-menu.js.map +7 -0
  136. package/build-module/components/content-only-controls/index.js +420 -0
  137. package/build-module/components/content-only-controls/index.js.map +7 -0
  138. package/build-module/components/content-only-controls/link/index.js +160 -0
  139. package/build-module/components/content-only-controls/link/index.js.map +7 -0
  140. package/build-module/components/content-only-controls/media/index.js +242 -0
  141. package/build-module/components/content-only-controls/media/index.js.map +7 -0
  142. package/build-module/components/content-only-controls/rich-text/index.js +160 -0
  143. package/build-module/components/content-only-controls/rich-text/index.js.map +7 -0
  144. package/build-module/components/content-only-controls/use-inspector-popover-placement.js +16 -0
  145. package/build-module/components/content-only-controls/use-inspector-popover-placement.js.map +7 -0
  146. package/build-module/components/font-family/index.js +1 -15
  147. package/build-module/components/font-family/index.js.map +2 -2
  148. package/build-module/components/global-styles/dimensions-panel.js +35 -2
  149. package/build-module/components/global-styles/dimensions-panel.js.map +2 -2
  150. package/build-module/components/global-styles/hooks.js +1 -1
  151. package/build-module/components/global-styles/hooks.js.map +2 -2
  152. package/build-module/components/global-styles/typography-panel.js +1 -2
  153. package/build-module/components/global-styles/typography-panel.js.map +2 -2
  154. package/build-module/components/inserter/media-tab/media-tab.js +2 -34
  155. package/build-module/components/inserter/media-tab/media-tab.js.map +2 -2
  156. package/build-module/components/inspector-controls-tabs/content-tab.js +7 -3
  157. package/build-module/components/inspector-controls-tabs/content-tab.js.map +2 -2
  158. package/build-module/components/inspector-controls-tabs/index.js +7 -1
  159. package/build-module/components/inspector-controls-tabs/index.js.map +2 -2
  160. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.js +1 -1
  161. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.js.map +2 -2
  162. package/build-module/components/link-control/index.js +16 -8
  163. package/build-module/components/link-control/index.js.map +2 -2
  164. package/build-module/components/list-view/block-select-button.js +14 -9
  165. package/build-module/components/list-view/block-select-button.js.map +2 -2
  166. package/build-module/components/list-view/block.js +9 -7
  167. package/build-module/components/list-view/block.js.map +2 -2
  168. package/build-module/components/media-placeholder/index.js +19 -36
  169. package/build-module/components/media-placeholder/index.js.map +2 -2
  170. package/build-module/components/media-placeholder/utils.js +35 -0
  171. package/build-module/components/media-placeholder/utils.js.map +7 -0
  172. package/build-module/components/media-replace-flow/index.js +24 -33
  173. package/build-module/components/media-replace-flow/index.js.map +2 -2
  174. package/build-module/components/use-block-commands/index.js +1 -1
  175. package/build-module/components/use-block-commands/index.js.map +2 -2
  176. package/build-module/components/use-block-display-information/index.js +21 -1
  177. package/build-module/components/use-block-display-information/index.js.map +3 -3
  178. package/build-module/components/use-block-drop-zone/index.js +1 -5
  179. package/build-module/components/use-block-drop-zone/index.js.map +2 -2
  180. package/build-module/hooks/block-bindings.js +57 -62
  181. package/build-module/hooks/block-bindings.js.map +2 -2
  182. package/build-module/hooks/dimensions.js +3 -3
  183. package/build-module/hooks/dimensions.js.map +2 -2
  184. package/build-module/hooks/metadata.js +1 -1
  185. package/build-module/hooks/metadata.js.map +2 -2
  186. package/build-module/hooks/use-content-only-section-edit.js +46 -0
  187. package/build-module/hooks/use-content-only-section-edit.js.map +7 -0
  188. package/build-module/hooks/utils.js +5 -1
  189. package/build-module/hooks/utils.js.map +2 -2
  190. package/build-module/layouts/constrained.js +2 -2
  191. package/build-module/layouts/constrained.js.map +2 -2
  192. package/build-module/private-apis.js +3 -3
  193. package/build-module/private-apis.js.map +2 -2
  194. package/build-module/store/private-keys.js +2 -0
  195. package/build-module/store/private-keys.js.map +2 -2
  196. package/build-module/store/private-selectors.js +37 -2
  197. package/build-module/store/private-selectors.js.map +2 -2
  198. package/build-module/store/selectors.js +6 -4
  199. package/build-module/store/selectors.js.map +2 -2
  200. package/build-module/utils/fit-text-utils.js +9 -1
  201. package/build-module/utils/fit-text-utils.js.map +2 -2
  202. package/build-style/content-rtl.css +3 -0
  203. package/build-style/content.css +3 -0
  204. package/build-style/style-rtl.css +145 -4
  205. package/build-style/style.css +145 -4
  206. package/package.json +38 -37
  207. package/src/components/block-inspector/edit-contents.js +10 -29
  208. package/src/components/block-inspector/index.js +4 -2
  209. package/src/components/block-list/block.js +6 -0
  210. package/src/components/block-list/content.scss +5 -0
  211. package/src/components/block-list/index.js +3 -1
  212. package/src/components/block-list/use-block-props/index.js +3 -1
  213. package/src/components/block-list/use-block-props/use-is-hovered.js +24 -12
  214. package/src/components/block-list/use-block-props/use-selected-block-event-handlers.js +34 -3
  215. package/src/components/block-lock/modal.js +6 -5
  216. package/src/components/block-lock/use-block-lock.js +10 -14
  217. package/src/components/block-patterns-list/stories/{index.story.js → index.story.jsx} +3 -1
  218. package/src/components/block-settings-menu-controls/edit-section-menu-item.js +39 -0
  219. package/src/components/block-settings-menu-controls/index.js +8 -1
  220. package/src/components/block-toolbar/block-toolbar-icon.js +14 -10
  221. package/src/components/block-tools/index.js +15 -2
  222. package/src/components/block-tools/style.scss +4 -0
  223. package/src/components/block-variation-transforms/index.js +96 -35
  224. package/src/components/block-visibility/toolbar.js +1 -1
  225. package/src/components/border-radius-control/single-input-control.js +1 -0
  226. package/src/components/content-only-controls/fields-dropdown-menu.js +53 -0
  227. package/src/components/content-only-controls/index.js +560 -0
  228. package/src/components/content-only-controls/link/index.js +200 -0
  229. package/src/components/content-only-controls/link/styles.scss +23 -0
  230. package/src/components/content-only-controls/media/index.js +306 -0
  231. package/src/components/content-only-controls/media/styles.scss +47 -0
  232. package/src/components/content-only-controls/rich-text/index.js +179 -0
  233. package/src/components/content-only-controls/rich-text/styles.scss +24 -0
  234. package/src/components/content-only-controls/styles.scss +44 -0
  235. package/src/components/content-only-controls/use-inspector-popover-placement.js +19 -0
  236. package/src/components/font-family/README.md +0 -9
  237. package/src/components/font-family/index.js +1 -16
  238. package/src/components/font-family/stories/{index.story.js → index.story.jsx} +0 -1
  239. package/src/components/global-styles/dimensions-panel.js +36 -0
  240. package/src/components/global-styles/hooks.js +1 -1
  241. package/src/components/global-styles/typography-panel.js +0 -1
  242. package/src/components/inserter/media-tab/media-tab.js +2 -44
  243. package/src/components/inspector-controls-tabs/content-tab.js +12 -4
  244. package/src/components/inspector-controls-tabs/index.js +4 -1
  245. package/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js +1 -5
  246. package/src/components/link-control/index.js +36 -12
  247. package/src/components/list-view/block-select-button.js +15 -10
  248. package/src/components/list-view/block.js +9 -7
  249. package/src/components/media-placeholder/index.js +21 -46
  250. package/src/components/media-placeholder/test/get-computed-accept-attribute.js +164 -0
  251. package/src/components/media-placeholder/utils.js +65 -0
  252. package/src/components/media-replace-flow/index.js +25 -42
  253. package/src/components/use-block-commands/index.js +1 -1
  254. package/src/components/use-block-display-information/index.js +30 -2
  255. package/src/components/use-block-drop-zone/index.js +1 -5
  256. package/src/hooks/block-bindings.js +71 -82
  257. package/src/hooks/dimensions.js +8 -3
  258. package/src/hooks/metadata.js +1 -1
  259. package/src/hooks/test/metadata.js +1 -1
  260. package/src/hooks/use-content-only-section-edit.js +63 -0
  261. package/src/hooks/utils.js +4 -0
  262. package/src/layouts/constrained.js +8 -2
  263. package/src/private-apis.js +2 -2
  264. package/src/store/private-keys.js +1 -0
  265. package/src/store/private-selectors.js +121 -5
  266. package/src/store/selectors.js +6 -4
  267. package/src/store/test/private-selectors.js +242 -0
  268. package/src/style.scss +1 -1
  269. package/src/utils/fit-text-utils.js +19 -1
  270. package/tsconfig.json +1 -0
  271. package/build/components/media-upload-modal/index.js +0 -29
  272. package/build/components/media-upload-modal/index.js.map +0 -7
  273. package/build-module/components/media-upload-modal/index.js +0 -8
  274. package/build-module/components/media-upload-modal/index.js.map +0 -7
  275. package/src/components/font-family/style.scss +0 -7
  276. package/src/components/media-upload-modal/index.js +0 -18
  277. /package/src/components/alignment-control/stories/{aliginment-toolbar.story.js → aliginment-toolbar.story.jsx} +0 -0
  278. /package/src/components/alignment-control/stories/{index.story.js → index.story.jsx} +0 -0
  279. /package/src/components/block-alignment-matrix-control/stories/{index.story.js → index.story.jsx} +0 -0
  280. /package/src/components/block-draggable/stories/{index.story.js → index.story.jsx} +0 -0
  281. /package/src/components/block-heading-level-dropdown/stories/{index.story.js → index.story.jsx} +0 -0
  282. /package/src/components/block-mover/stories/{index.story.js → index.story.jsx} +0 -0
  283. /package/src/components/block-title/stories/{index.story.js → index.story.jsx} +0 -0
  284. /package/src/components/border-radius-control/stories/{index.story.js → index.story.jsx} +0 -0
  285. /package/src/components/date-format-picker/stories/{index.story.js → index.story.jsx} +0 -0
  286. /package/src/components/dimensions-tool/stories/{aspect-ratio-tool.story.js → aspect-ratio-tool.story.jsx} +0 -0
  287. /package/src/components/dimensions-tool/stories/{index.story.js → index.story.jsx} +0 -0
  288. /package/src/components/dimensions-tool/stories/{scale-tool.story.js → scale-tool.story.jsx} +0 -0
  289. /package/src/components/dimensions-tool/stories/{width-height-tool.story.js → width-height-tool.story.jsx} +0 -0
  290. /package/src/components/height-control/stories/{index.story.js → index.story.jsx} +0 -0
  291. /package/src/components/inserter/stories/{index.story.js → index.story.jsx} +0 -0
  292. /package/src/components/line-height-control/stories/{index.story.js → index.story.jsx} +0 -0
  293. /package/src/components/plain-text/stories/{index.story.js → index.story.jsx} +0 -0
  294. /package/src/components/resolution-tool/stories/{index.story.js → index.story.jsx} +0 -0
  295. /package/src/components/tabbed-sidebar/stories/{index.story.js → index.story.jsx} +0 -0
  296. /package/src/components/text-alignment-control/stories/{index.story.js → index.story.jsx} +0 -0
  297. /package/src/components/text-decoration-control/stories/{index.story.js → index.story.jsx} +0 -0
  298. /package/src/components/text-transform-control/stories/{index.story.js → index.story.jsx} +0 -0
  299. /package/src/components/unit-control/stories/{index.story.js → index.story.jsx} +0 -0
  300. /package/src/components/url-popover/stories/{index.story.js → index.story.jsx} +0 -0
  301. /package/src/components/warning/stories/{index.story.js → index.story.jsx} +0 -0
  302. /package/src/components/writing-mode-control/stories/{index.story.js → index.story.jsx} +0 -0
@@ -12,7 +12,13 @@ import {
12
12
  privateApis as componentsPrivateApis,
13
13
  } from '@wordpress/components';
14
14
  import { forwardRef } from '@wordpress/element';
15
- import { Icon, lockSmall as lock, pinSmall, unseen } from '@wordpress/icons';
15
+ import {
16
+ Icon,
17
+ lockSmall as lock,
18
+ pinSmall,
19
+ unseen,
20
+ symbol,
21
+ } from '@wordpress/icons';
16
22
  import { SPACE, ENTER } from '@wordpress/keycodes';
17
23
  import { useSelect } from '@wordpress/data';
18
24
  import { hasBlockSupport } from '@wordpress/blocks';
@@ -55,29 +61,28 @@ function ListViewBlockSelectButton(
55
61
  context: 'list-view',
56
62
  } );
57
63
  const { isLocked } = useBlockLock( clientId );
58
- const { canToggleBlockVisibility, isBlockHidden, isContentOnly } =
64
+ const { canToggleBlockVisibility, isBlockHidden, hasPatternName } =
59
65
  useSelect(
60
66
  ( select ) => {
61
- const { getBlockName } = select( blockEditorStore );
67
+ const { getBlockName, getBlockAttributes } =
68
+ select( blockEditorStore );
62
69
  const { isBlockHidden: _isBlockHidden } = unlock(
63
70
  select( blockEditorStore )
64
71
  );
72
+ const blockAttributes = getBlockAttributes( clientId );
65
73
  return {
66
74
  canToggleBlockVisibility: hasBlockSupport(
67
75
  getBlockName( clientId ),
68
- 'blockVisibility',
76
+ 'visibility',
69
77
  true
70
78
  ),
71
79
  isBlockHidden: _isBlockHidden( clientId ),
72
- isContentOnly:
73
- select( blockEditorStore ).getBlockEditingMode(
74
- clientId
75
- ) === 'contentOnly',
80
+ hasPatternName: !! blockAttributes?.metadata?.patternName,
76
81
  };
77
82
  },
78
83
  [ clientId ]
79
84
  );
80
- const shouldShowLockIcon = isLocked && ! isContentOnly;
85
+ const shouldShowLockIcon = isLocked;
81
86
  const shouldShowBlockVisibilityIcon =
82
87
  canToggleBlockVisibility && isBlockHidden;
83
88
  const isSticky = blockInformation?.positionType === 'sticky';
@@ -123,7 +128,7 @@ function ListViewBlockSelectButton(
123
128
  >
124
129
  <ListViewExpander onClick={ onToggleExpanded } />
125
130
  <BlockIcon
126
- icon={ blockInformation?.icon }
131
+ icon={ hasPatternName ? symbol : blockInformation?.icon }
127
132
  showColors
128
133
  context="list-view"
129
134
  />
@@ -80,7 +80,7 @@ function ListViewBlock( {
80
80
  const [ isHovered, setIsHovered ] = useState( false );
81
81
  const [ settingsAnchorRect, setSettingsAnchorRect ] = useState();
82
82
 
83
- const { isLocked, canEdit, canMove } = useBlockLock( clientId );
83
+ const { isLocked } = useBlockLock( clientId );
84
84
 
85
85
  const isFirstSelectedBlock =
86
86
  isSelected && selectedClientIds[ 0 ] === clientId;
@@ -112,6 +112,8 @@ function ListViewBlock( {
112
112
  getBlockOrder,
113
113
  getBlockParents,
114
114
  getBlocksByClientId,
115
+ canEditBlock,
116
+ canMoveBlock,
115
117
  canRemoveBlocks,
116
118
  isGroupable,
117
119
  } = useSelect( blockEditorStore );
@@ -375,10 +377,10 @@ function ListViewBlock( {
375
377
  event.preventDefault();
376
378
  const { blocksToUpdate } = getBlocksToUpdate();
377
379
  const blocks = getBlocksByClientId( blocksToUpdate );
378
- const canToggleBlockVisibility = blocks.every( ( blockToUpdate ) =>
379
- hasBlockSupport( blockToUpdate.name, 'blockVisibility', true )
380
+ const canToggleVisibility = blocks.every( ( blockToUpdate ) =>
381
+ hasBlockSupport( blockToUpdate.name, 'visibility', true )
380
382
  );
381
- if ( ! canToggleBlockVisibility ) {
383
+ if ( ! canToggleVisibility ) {
382
384
  return;
383
385
  }
384
386
  const hasHiddenBlock = blocks.some(
@@ -553,7 +555,7 @@ function ListViewBlock( {
553
555
  'is-dragging': isDragged,
554
556
  'has-single-cell': ! showBlockActions,
555
557
  'is-synced': blockInformation?.isSynced,
556
- 'is-draggable': canMove,
558
+ 'is-draggable': canMoveBlock,
557
559
  'is-displacement-normal': displacement === 'normal',
558
560
  'is-displacement-up': displacement === 'up',
559
561
  'is-displacement-down': displacement === 'down',
@@ -588,7 +590,7 @@ function ListViewBlock( {
588
590
  path={ path }
589
591
  id={ `list-view-${ listViewInstanceId }-block-${ clientId }` }
590
592
  data-block={ clientId }
591
- data-expanded={ canEdit ? isExpanded : undefined }
593
+ data-expanded={ canEditBlock ? isExpanded : undefined }
592
594
  ref={ rowRef }
593
595
  >
594
596
  <TreeGridCell
@@ -614,7 +616,7 @@ function ListViewBlock( {
614
616
  currentlyEditingBlockInCanvas ? 0 : tabIndex
615
617
  }
616
618
  onFocus={ onFocus }
617
- isExpanded={ canEdit ? isExpanded : undefined }
619
+ isExpanded={ canEditBlock ? isExpanded : undefined }
618
620
  selectedClientIds={ selectedClientIds }
619
621
  ariaDescribedBy={ descriptionId }
620
622
  />
@@ -16,7 +16,7 @@ import {
16
16
  withFilters,
17
17
  } from '@wordpress/components';
18
18
  import { __, _x } from '@wordpress/i18n';
19
- import { useState, useEffect } from '@wordpress/element';
19
+ import { useState, useEffect, useMemo } from '@wordpress/element';
20
20
  import { useSelect } from '@wordpress/data';
21
21
  import { keyboardReturn } from '@wordpress/icons';
22
22
  import deprecated from '@wordpress/deprecated';
@@ -25,53 +25,14 @@ import deprecated from '@wordpress/deprecated';
25
25
  * Internal dependencies
26
26
  */
27
27
  import MediaUpload from '../media-upload';
28
- import MediaUploadModal from '../media-upload-modal';
29
28
  import MediaUploadCheck from '../media-upload/check';
30
29
  import URLPopover from '../url-popover';
31
30
  import { store as blockEditorStore } from '../../store';
32
31
  import { parseDropEvent } from '../use-on-block-drop';
32
+ import { getComputedAcceptAttribute } from './utils';
33
33
 
34
34
  const noop = () => {};
35
35
 
36
- /**
37
- * Conditional Media component that uses MediaUploadModal when experiment is enabled,
38
- * otherwise falls back to MediaUpload.
39
- *
40
- * @param {Object} root0 Component props.
41
- * @param {Function} root0.render Render prop function that receives { open } object.
42
- * @return {JSX.Element} The component.
43
- */
44
- function ConditionalMediaUpload( { render, ...props } ) {
45
- const [ isModalOpen, setIsModalOpen ] = useState( false );
46
- const mediaUpload = useSelect( ( select ) => {
47
- const { getSettings } = select( blockEditorStore );
48
- return getSettings().mediaUpload;
49
- }, [] );
50
-
51
- if ( window.__experimentalDataViewsMediaModal ) {
52
- return (
53
- <>
54
- { render && render( { open: () => setIsModalOpen( true ) } ) }
55
- <MediaUploadModal
56
- { ...props }
57
- isOpen={ isModalOpen }
58
- onClose={ () => {
59
- setIsModalOpen( false );
60
- props.onClose?.();
61
- } }
62
- onSelect={ ( media ) => {
63
- setIsModalOpen( false );
64
- props.onSelect?.( media );
65
- } }
66
- onUpload={ mediaUpload }
67
- />
68
- </>
69
- );
70
- }
71
-
72
- return <MediaUpload { ...props } render={ render } />;
73
- }
74
-
75
36
  const InsertFromURLPopover = ( {
76
37
  src,
77
38
  onChange,
@@ -190,9 +151,13 @@ export function MediaPlaceholder( {
190
151
  } );
191
152
  }
192
153
 
193
- const mediaUpload = useSelect( ( select ) => {
154
+ const { mediaUpload, allowedMimeTypes } = useSelect( ( select ) => {
194
155
  const { getSettings } = select( blockEditorStore );
195
- return getSettings().mediaUpload;
156
+ const settings = getSettings();
157
+ return {
158
+ mediaUpload: settings.mediaUpload,
159
+ allowedMimeTypes: settings.allowedMimeTypes,
160
+ };
196
161
  }, [] );
197
162
  const [ src, setSrc ] = useState( '' );
198
163
 
@@ -200,6 +165,16 @@ export function MediaPlaceholder( {
200
165
  setSrc( value?.src ?? '' );
201
166
  }, [ value?.src ] );
202
167
 
168
+ const computedAccept = useMemo(
169
+ () =>
170
+ getComputedAcceptAttribute(
171
+ allowedTypes,
172
+ allowedMimeTypes,
173
+ accept
174
+ ),
175
+ [ allowedTypes, allowedMimeTypes, accept ]
176
+ );
177
+
203
178
  const onlyAllowsImages = () => {
204
179
  if ( ! allowedTypes || allowedTypes.length === 0 ) {
205
180
  return false;
@@ -488,7 +463,7 @@ export function MediaPlaceholder( {
488
463
  };
489
464
  const libraryButton = mediaLibraryButton ?? defaultButton;
490
465
  const uploadMediaLibraryButton = (
491
- <ConditionalMediaUpload
466
+ <MediaUpload
492
467
  addToGallery={ addToGallery }
493
468
  gallery={ multiple && onlyAllowsImages() }
494
469
  multiple={ multiple }
@@ -510,7 +485,7 @@ export function MediaPlaceholder( {
510
485
  { renderDropZone() }
511
486
  <FormFileUpload
512
487
  onChange={ onUpload }
513
- accept={ accept }
488
+ accept={ computedAccept }
514
489
  multiple={ !! multiple }
515
490
  render={ ( { openFileDialog } ) => {
516
491
  const content = (
@@ -558,7 +533,7 @@ export function MediaPlaceholder( {
558
533
  </Button>
559
534
  ) }
560
535
  onChange={ onUpload }
561
- accept={ accept }
536
+ accept={ computedAccept }
562
537
  multiple={ !! multiple }
563
538
  />
564
539
  { uploadMediaLibraryButton }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { getComputedAcceptAttribute } from '../utils';
5
+
6
+ describe( 'getComputedAcceptAttribute', () => {
7
+ it( 'returns the accept prop as-is when explicitly provided', () => {
8
+ const result = getComputedAcceptAttribute(
9
+ [ 'image' ],
10
+ { 'jpg|jpeg': 'image/jpeg', png: 'image/png' },
11
+ 'image/png,image/jpeg'
12
+ );
13
+ expect( result ).toBe( 'image/png,image/jpeg' );
14
+ } );
15
+
16
+ it( 'returns undefined when no allowedTypes are specified', () => {
17
+ const result = getComputedAcceptAttribute(
18
+ [],
19
+ { 'jpg|jpeg': 'image/jpeg', png: 'image/png' },
20
+ undefined
21
+ );
22
+ expect( result ).toBeUndefined();
23
+ } );
24
+
25
+ it( 'returns undefined when allowedTypes is null or undefined', () => {
26
+ const result = getComputedAcceptAttribute(
27
+ null,
28
+ { 'jpg|jpeg': 'image/jpeg', png: 'image/png' },
29
+ undefined
30
+ );
31
+ expect( result ).toBeUndefined();
32
+ } );
33
+
34
+ it( 'returns wildcard format when allowedMimeTypes is not available', () => {
35
+ const result = getComputedAcceptAttribute(
36
+ [ 'image', 'video' ],
37
+ undefined,
38
+ undefined
39
+ );
40
+ expect( result ).toBe( 'image/*,video/*' );
41
+ } );
42
+
43
+ it( 'returns wildcard format when allowedMimeTypes is not an object', () => {
44
+ const result = getComputedAcceptAttribute(
45
+ [ 'image' ],
46
+ 'not-an-object',
47
+ undefined
48
+ );
49
+ expect( result ).toBe( 'image/*' );
50
+ } );
51
+
52
+ it( 'returns specific MIME types when they match allowedTypes', () => {
53
+ const result = getComputedAcceptAttribute(
54
+ [ 'image' ],
55
+ {
56
+ 'jpg|jpeg': 'image/jpeg',
57
+ png: 'image/png',
58
+ mp4: 'video/mp4',
59
+ },
60
+ undefined
61
+ );
62
+ expect( result ).toBe( 'image/jpeg,image/png' );
63
+ } );
64
+
65
+ it( 'supports allowedTypes with full MIME type format', () => {
66
+ const result = getComputedAcceptAttribute(
67
+ [ 'image/png', 'video/mp4' ],
68
+ {
69
+ png: 'image/png',
70
+ 'jpg|jpeg': 'image/jpeg',
71
+ mp4: 'video/mp4',
72
+ },
73
+ undefined
74
+ );
75
+ expect( result ).toBe( 'image/png,video/mp4' );
76
+ } );
77
+
78
+ it( 'falls back to wildcard when specific MIME types are not found', () => {
79
+ const result = getComputedAcceptAttribute( [ 'image' ], {}, undefined );
80
+ expect( result ).toBe( 'image/*' );
81
+ } );
82
+
83
+ it( 'filters MIME types correctly when mixed with other types', () => {
84
+ const result = getComputedAcceptAttribute(
85
+ [ 'image', 'audio' ],
86
+ {
87
+ 'jpg|jpeg': 'image/jpeg',
88
+ png: 'image/png',
89
+ mp4: 'video/mp4',
90
+ mp3: 'audio/mp3',
91
+ wav: 'audio/wav',
92
+ },
93
+ undefined
94
+ );
95
+ expect( result ).toBe( 'image/jpeg,image/png,audio/mp3,audio/wav' );
96
+ } );
97
+
98
+ it( 'returns undefined when allowedMimeTypes is invalid and allowedTypes is empty', () => {
99
+ const result = getComputedAcceptAttribute( [], null, undefined );
100
+ expect( result ).toBeUndefined();
101
+ } );
102
+
103
+ it( 'handles empty allowedMimeTypes object', () => {
104
+ const result = getComputedAcceptAttribute( [ 'image' ], {}, undefined );
105
+ expect( result ).toBe( 'image/*' );
106
+ } );
107
+
108
+ it( 'preserves MIME type order from allowedMimeTypes', () => {
109
+ const mimeTypes = {
110
+ 'jpg|jpeg': 'image/jpeg',
111
+ png: 'image/png',
112
+ gif: 'image/gif',
113
+ };
114
+ const result = getComputedAcceptAttribute(
115
+ [ 'image' ],
116
+ mimeTypes,
117
+ undefined
118
+ );
119
+ // The exact order depends on Object.entries, which preserves insertion order in modern JS
120
+ expect( result ).toContain( 'image/jpeg' );
121
+ expect( result ).toContain( 'image/png' );
122
+ expect( result ).toContain( 'image/gif' );
123
+ } );
124
+
125
+ it( 'handles mixed allowedTypes with and without MIME type format', () => {
126
+ const result = getComputedAcceptAttribute(
127
+ [ 'image', 'video/mp4' ],
128
+ {
129
+ png: 'image/png',
130
+ 'jpg|jpeg': 'image/jpeg',
131
+ mp4: 'video/mp4',
132
+ webm: 'video/webm',
133
+ },
134
+ undefined
135
+ );
136
+ // 'image' matches image/* types and 'video/mp4' matches exact MIME type
137
+ expect( result ).toBe( 'image/png,image/jpeg,video/mp4' );
138
+ } );
139
+
140
+ it( 'returns single MIME type without comma', () => {
141
+ const result = getComputedAcceptAttribute(
142
+ [ 'image' ],
143
+ {
144
+ png: 'image/png',
145
+ },
146
+ undefined
147
+ );
148
+ expect( result ).toBe( 'image/png' );
149
+ } );
150
+
151
+ it( 'prioritizes specific MIME types over wildcard', () => {
152
+ const result = getComputedAcceptAttribute(
153
+ [ 'image' ],
154
+ {
155
+ 'jpg|jpeg': 'image/jpeg',
156
+ png: 'image/png',
157
+ },
158
+ undefined
159
+ );
160
+ // Should return specific types, not 'image/*'
161
+ expect( result ).not.toBe( 'image/*' );
162
+ expect( result ).toBe( 'image/jpeg,image/png' );
163
+ } );
164
+ } );
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Computes the accept attribute for file inputs based on allowed types
3
+ * and server-supported MIME types.
4
+ *
5
+ * This ensures users can only select file types that the server can handle,
6
+ * preventing upload failures (e.g., HEIC files when server lacks support).
7
+ *
8
+ * @param {Array} allowedTypes - List of allowed media types (e.g., ['image', 'video']).
9
+ * @param {Object} allowedMimeTypes - Map of allowed MIME types from server settings.
10
+ * @param {string} accept - Optional explicit accept attribute to use.
11
+ *
12
+ * @return {string|undefined} Computed accept attribute value, or undefined if no restrictions apply.
13
+ */
14
+ export function getComputedAcceptAttribute(
15
+ allowedTypes,
16
+ allowedMimeTypes,
17
+ accept
18
+ ) {
19
+ // If accept prop is explicitly provided, use it as is.
20
+ if ( accept ) {
21
+ return accept;
22
+ }
23
+
24
+ // If allowedMimeTypes is not available, fall back to wildcard.
25
+ if (
26
+ ! allowedMimeTypes ||
27
+ typeof allowedMimeTypes !== 'object' ||
28
+ Object.keys( allowedMimeTypes ).length === 0
29
+ ) {
30
+ if ( allowedTypes && allowedTypes.length > 0 ) {
31
+ return allowedTypes.map( ( type ) => `${ type }/*` ).join( ',' );
32
+ }
33
+ return undefined;
34
+ }
35
+
36
+ // If no allowedTypes specified, we can't filter, so return undefined.
37
+ if ( ! allowedTypes || allowedTypes.length === 0 ) {
38
+ return undefined;
39
+ }
40
+
41
+ // Build a list of specific MIME types based on allowedTypes.
42
+ const acceptedMimeTypes = [];
43
+
44
+ for ( const [ , mimeType ] of Object.entries( allowedMimeTypes ) ) {
45
+ // Check if this MIME type matches any of the allowedTypes.
46
+ const isAllowed = allowedTypes.some( ( allowedType ) => {
47
+ // Support both 'image' and 'image/jpeg' formats.
48
+ if ( allowedType.includes( '/' ) ) {
49
+ return mimeType === allowedType;
50
+ }
51
+ return mimeType.startsWith( `${ allowedType }/` );
52
+ } );
53
+
54
+ if ( isAllowed ) {
55
+ acceptedMimeTypes.push( mimeType );
56
+ }
57
+ }
58
+
59
+ // If we found specific MIME types, use them. Otherwise fall back to wildcard.
60
+ if ( acceptedMimeTypes.length > 0 ) {
61
+ return acceptedMimeTypes.join( ',' );
62
+ }
63
+
64
+ return allowedTypes.map( ( type ) => `${ type }/*` ).join( ',' );
65
+ }
@@ -12,7 +12,6 @@ import {
12
12
  ToolbarButton,
13
13
  } from '@wordpress/components';
14
14
  import { useSelect, withDispatch } from '@wordpress/data';
15
- import { useState } from '@wordpress/element';
16
15
  import { DOWN } from '@wordpress/keycodes';
17
16
  import {
18
17
  postFeaturedImage,
@@ -22,55 +21,20 @@ import {
22
21
  import { compose } from '@wordpress/compose';
23
22
  import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
24
23
  import { store as noticesStore } from '@wordpress/notices';
24
+ import { useMemo } from '@wordpress/element';
25
25
 
26
26
  /**
27
27
  * Internal dependencies
28
28
  */
29
29
  import MediaUpload from '../media-upload';
30
- import MediaUploadModal from '../media-upload-modal';
31
30
  import MediaUploadCheck from '../media-upload/check';
32
31
  import LinkControl from '../link-control';
33
32
  import { store as blockEditorStore } from '../../store';
33
+ import { getComputedAcceptAttribute } from '../media-placeholder/utils';
34
34
 
35
35
  const noop = () => {};
36
36
  let uniqueId = 0;
37
37
 
38
- /**
39
- * Conditional Media component that uses MediaUploadModal when experiment is enabled,
40
- * otherwise falls back to MediaUpload.
41
- *
42
- * @param {Object} root0 Component props.
43
- * @param {Function} root0.render Render prop function that receives { open } object.
44
- * @return {JSX.Element} The component.
45
- */
46
- function ConditionalMediaUpload( { render, ...props } ) {
47
- const [ isModalOpen, setIsModalOpen ] = useState( false );
48
- const { getSettings } = useSelect( blockEditorStore );
49
-
50
- if ( window.__experimentalDataViewsMediaModal ) {
51
- return (
52
- <>
53
- { render && render( { open: () => setIsModalOpen( true ) } ) }
54
- <MediaUploadModal
55
- { ...props }
56
- isOpen={ isModalOpen }
57
- onClose={ () => {
58
- setIsModalOpen( false );
59
- props.onClose?.();
60
- } }
61
- onSelect={ ( media ) => {
62
- setIsModalOpen( false );
63
- props.onSelect?.( media );
64
- } }
65
- onUpload={ getSettings().mediaUpload }
66
- />
67
- </>
68
- );
69
- }
70
-
71
- return <MediaUpload { ...props } render={ render } />;
72
- }
73
-
74
38
  const MediaReplaceFlow = ( {
75
39
  mediaURL,
76
40
  mediaId,
@@ -93,10 +57,28 @@ const MediaReplaceFlow = ( {
93
57
  handleUpload = true,
94
58
  popoverProps,
95
59
  renderToggle,
60
+ className,
96
61
  } ) => {
97
- const { getSettings } = useSelect( blockEditorStore );
62
+ const { mediaUpload, allowedMimeTypes } = useSelect( ( select ) => {
63
+ const { getSettings } = select( blockEditorStore );
64
+ const settings = getSettings();
65
+ return {
66
+ mediaUpload: settings.mediaUpload,
67
+ allowedMimeTypes: settings.allowedMimeTypes,
68
+ };
69
+ }, [] );
98
70
  const errorNoticeID = `block-editor/media-replace-flow/error-notice/${ ++uniqueId }`;
99
71
 
72
+ const computedAccept = useMemo(
73
+ () =>
74
+ getComputedAcceptAttribute(
75
+ allowedTypes,
76
+ allowedMimeTypes,
77
+ accept
78
+ ),
79
+ [ allowedTypes, allowedMimeTypes, accept ]
80
+ );
81
+
100
82
  const onUploadError = ( message ) => {
101
83
  const safeMessage = stripHTML( message );
102
84
  if ( onError ) {
@@ -136,7 +118,7 @@ const MediaReplaceFlow = ( {
136
118
  return onSelect( files );
137
119
  }
138
120
  onFilesUpload( files );
139
- getSettings().mediaUpload( {
121
+ mediaUpload( {
140
122
  allowedTypes,
141
123
  filesList: files,
142
124
  onFileChange: ( [ media ] ) => {
@@ -169,6 +151,7 @@ const MediaReplaceFlow = ( {
169
151
  return (
170
152
  <Dropdown
171
153
  popoverProps={ popoverProps }
154
+ className={ className }
172
155
  contentClassName="block-editor-media-replace-flow__options"
173
156
  renderToggle={ ( { isOpen, onToggle } ) => {
174
157
  if ( renderToggle ) {
@@ -195,7 +178,7 @@ const MediaReplaceFlow = ( {
195
178
  <>
196
179
  <NavigableMenu className="block-editor-media-replace-flow__media-upload-menu">
197
180
  <MediaUploadCheck>
198
- <ConditionalMediaUpload
181
+ <MediaUpload
199
182
  gallery={ gallery }
200
183
  addToGallery={ addToGallery }
201
184
  multiple={ multiple }
@@ -217,7 +200,7 @@ const MediaReplaceFlow = ( {
217
200
  onChange={ ( event ) => {
218
201
  uploadFiles( event, onClose );
219
202
  } }
220
- accept={ accept }
203
+ accept={ computedAccept }
221
204
  multiple={ !! multiple }
222
205
  render={ ( { openFileDialog } ) => {
223
206
  return (
@@ -224,7 +224,7 @@ const getQuickActionsCommands = () =>
224
224
  const canRemove = canRemoveBlocks( clientIds );
225
225
 
226
226
  const canToggleBlockVisibility = blocks.every( ( { clientId } ) =>
227
- hasBlockSupport( getBlockName( clientId ), 'blockVisibility', true )
227
+ hasBlockSupport( getBlockName( clientId ), 'visibility', true )
228
228
  );
229
229
 
230
230
  const commands = [];
@@ -9,6 +9,7 @@ import {
9
9
  __experimentalGetBlockLabel as getBlockLabel,
10
10
  } from '@wordpress/blocks';
11
11
  import { __ } from '@wordpress/i18n';
12
+ import { symbol } from '@wordpress/icons';
12
13
 
13
14
  /**
14
15
  * Internal dependencies
@@ -71,8 +72,11 @@ export default function useBlockDisplayInformation( clientId ) {
71
72
  if ( ! clientId ) {
72
73
  return null;
73
74
  }
74
- const { getBlockName, getBlockAttributes } =
75
- select( blockEditorStore );
75
+ const {
76
+ getBlockName,
77
+ getBlockAttributes,
78
+ __experimentalGetParsedPattern,
79
+ } = select( blockEditorStore );
76
80
  const { getBlockType, getActiveBlockVariation } =
77
81
  select( blocksStore );
78
82
  const blockName = getBlockName( clientId );
@@ -81,6 +85,30 @@ export default function useBlockDisplayInformation( clientId ) {
81
85
  return null;
82
86
  }
83
87
  const attributes = getBlockAttributes( clientId );
88
+
89
+ // Check if this block is a pattern
90
+ const patternName = attributes?.metadata?.patternName;
91
+
92
+ if (
93
+ patternName &&
94
+ window?.__experimentalContentOnlyPatternInsertion
95
+ ) {
96
+ const pattern = __experimentalGetParsedPattern( patternName );
97
+
98
+ const positionLabel = getPositionTypeLabel( attributes );
99
+ return {
100
+ isSynced: false,
101
+ title: __( 'Pattern' ),
102
+ icon: symbol,
103
+ description:
104
+ pattern?.description || __( 'A block pattern.' ),
105
+ anchor: attributes?.anchor,
106
+ positionLabel,
107
+ positionType: attributes?.style?.position?.type,
108
+ name: pattern?.title || attributes?.metadata?.name,
109
+ };
110
+ }
111
+
84
112
  const match = getActiveBlockVariation( blockName, attributes );
85
113
  const isSynced =
86
114
  isReusableBlock( blockType ) || isTemplatePart( blockType );
@@ -415,11 +415,7 @@ export default function useBlockDropZone( {
415
415
  // Filter out blocks that are hidden
416
416
  .filter( ( block ) => {
417
417
  return ! (
418
- hasBlockSupport(
419
- block.name,
420
- 'blockVisibility',
421
- true
422
- ) &&
418
+ hasBlockSupport( block.name, 'visibility', true ) &&
423
419
  block.attributes?.metadata?.blockVisibility ===
424
420
  false
425
421
  );