@wordpress/block-editor 12.22.0 → 12.23.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 (273) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +4 -0
  3. package/build/components/block-list/block.js +11 -3
  4. package/build/components/block-list/block.js.map +1 -1
  5. package/build/components/block-mover/button.js +4 -1
  6. package/build/components/block-mover/button.js.map +1 -1
  7. package/build/components/block-mover/index.js +5 -1
  8. package/build/components/block-mover/index.js.map +1 -1
  9. package/build/components/block-patterns-list/index.js +4 -1
  10. package/build/components/block-patterns-list/index.js.map +1 -1
  11. package/build/components/block-settings-menu/block-settings-dropdown.js +7 -3
  12. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  13. package/build/components/block-switcher/index.js +4 -3
  14. package/build/components/block-switcher/index.js.map +1 -1
  15. package/build/components/block-toolbar/index.js +5 -4
  16. package/build/components/block-toolbar/index.js.map +1 -1
  17. package/build/components/block-toolbar/shuffle.js +18 -9
  18. package/build/components/block-toolbar/shuffle.js.map +1 -1
  19. package/build/components/block-tools/block-selection-button.js +48 -8
  20. package/build/components/block-tools/block-selection-button.js.map +1 -1
  21. package/build/components/block-tools/index.js +14 -2
  22. package/build/components/block-tools/index.js.map +1 -1
  23. package/build/components/global-styles/advanced-panel.js +9 -2
  24. package/build/components/global-styles/advanced-panel.js.map +1 -1
  25. package/build/components/global-styles/background-panel.js +444 -0
  26. package/build/components/global-styles/background-panel.js.map +1 -0
  27. package/build/components/global-styles/color-panel.js +2 -1
  28. package/build/components/global-styles/color-panel.js.map +1 -1
  29. package/build/components/global-styles/get-global-styles-changes.js +3 -0
  30. package/build/components/global-styles/get-global-styles-changes.js.map +1 -1
  31. package/build/components/global-styles/hooks.js +1 -1
  32. package/build/components/global-styles/hooks.js.map +1 -1
  33. package/build/components/global-styles/index.js +13 -0
  34. package/build/components/global-styles/index.js.map +1 -1
  35. package/build/components/global-styles/use-global-styles-output.js +15 -14
  36. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  37. package/build/components/global-styles/utils.js +2 -1
  38. package/build/components/global-styles/utils.js.map +1 -1
  39. package/build/components/iframe/index.js +9 -4
  40. package/build/components/iframe/index.js.map +1 -1
  41. package/build/components/inserter/block-patterns-tab/index.js.map +1 -1
  42. package/build/components/inserter/block-patterns-tab/pattern-category-preview-panel.js +5 -0
  43. package/build/components/inserter/block-patterns-tab/pattern-category-preview-panel.js.map +1 -1
  44. package/build/components/inserter/library.js +2 -0
  45. package/build/components/inserter/library.js.map +1 -1
  46. package/build/components/inserter/menu.js +8 -2
  47. package/build/components/inserter/menu.js.map +1 -1
  48. package/build/components/inserter/search-items.js +36 -15
  49. package/build/components/inserter/search-items.js.map +1 -1
  50. package/build/components/keyboard-shortcuts/index.js +11 -0
  51. package/build/components/keyboard-shortcuts/index.js.map +1 -1
  52. package/build/components/list-view/block-select-button.js +16 -0
  53. package/build/components/list-view/block-select-button.js.map +1 -1
  54. package/build/components/list-view/block.js +1 -1
  55. package/build/components/list-view/block.js.map +1 -1
  56. package/build/components/list-view/index.js +17 -2
  57. package/build/components/list-view/index.js.map +1 -1
  58. package/build/components/list-view/use-list-view-collapse-items.js +47 -0
  59. package/build/components/list-view/use-list-view-collapse-items.js.map +1 -0
  60. package/build/components/rich-text/index.js +14 -11
  61. package/build/components/rich-text/index.js.map +1 -1
  62. package/build/components/rich-text/index.native.js +17 -11
  63. package/build/components/rich-text/index.native.js.map +1 -1
  64. package/build/components/rich-text/native/get-format-colors.native.js +1 -1
  65. package/build/components/rich-text/native/get-format-colors.native.js.map +1 -1
  66. package/build/components/rich-text/native/index.native.js +2 -2
  67. package/build/components/rich-text/native/index.native.js.map +1 -1
  68. package/build/components/rich-text/with-deprecations.js +0 -3
  69. package/build/components/rich-text/with-deprecations.js.map +1 -1
  70. package/build/components/url-popover/image-url-input-ui.js +50 -36
  71. package/build/components/url-popover/image-url-input-ui.js.map +1 -1
  72. package/build/components/use-block-display-information/index.js +4 -6
  73. package/build/components/use-block-display-information/index.js.map +1 -1
  74. package/build/hooks/anchor.js +2 -2
  75. package/build/hooks/anchor.js.map +1 -1
  76. package/build/hooks/background.js +70 -424
  77. package/build/hooks/background.js.map +1 -1
  78. package/build/hooks/index.js +7 -0
  79. package/build/hooks/index.js.map +1 -1
  80. package/build/hooks/use-zoom-out.js +47 -0
  81. package/build/hooks/use-zoom-out.js.map +1 -0
  82. package/build/index.js +7 -0
  83. package/build/index.js.map +1 -1
  84. package/build/private-apis.js +6 -1
  85. package/build/private-apis.js.map +1 -1
  86. package/build/private-apis.native.js +3 -1
  87. package/build/private-apis.native.js.map +1 -1
  88. package/build/store/private-actions.js +13 -0
  89. package/build/store/private-actions.js.map +1 -1
  90. package/build/store/private-keys.js +2 -1
  91. package/build/store/private-keys.js.map +1 -1
  92. package/build/store/private-selectors.js +24 -3
  93. package/build/store/private-selectors.js.map +1 -1
  94. package/build/store/reducer.js +22 -0
  95. package/build/store/reducer.js.map +1 -1
  96. package/build/store/selectors.js +34 -32
  97. package/build/store/selectors.js.map +1 -1
  98. package/build/store/utils.js +7 -1
  99. package/build/store/utils.js.map +1 -1
  100. package/build/utils/transform-styles/index.js +2 -1
  101. package/build/utils/transform-styles/index.js.map +1 -1
  102. package/build-module/components/block-list/block.js +11 -3
  103. package/build-module/components/block-list/block.js.map +1 -1
  104. package/build-module/components/block-mover/button.js +4 -1
  105. package/build-module/components/block-mover/button.js.map +1 -1
  106. package/build-module/components/block-mover/index.js +5 -1
  107. package/build-module/components/block-mover/index.js.map +1 -1
  108. package/build-module/components/block-patterns-list/index.js +4 -1
  109. package/build-module/components/block-patterns-list/index.js.map +1 -1
  110. package/build-module/components/block-settings-menu/block-settings-dropdown.js +7 -3
  111. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  112. package/build-module/components/block-switcher/index.js +4 -3
  113. package/build-module/components/block-switcher/index.js.map +1 -1
  114. package/build-module/components/block-toolbar/index.js +5 -4
  115. package/build-module/components/block-toolbar/index.js.map +1 -1
  116. package/build-module/components/block-toolbar/shuffle.js +18 -9
  117. package/build-module/components/block-toolbar/shuffle.js.map +1 -1
  118. package/build-module/components/block-tools/block-selection-button.js +50 -10
  119. package/build-module/components/block-tools/block-selection-button.js.map +1 -1
  120. package/build-module/components/block-tools/index.js +14 -2
  121. package/build-module/components/block-tools/index.js.map +1 -1
  122. package/build-module/components/global-styles/advanced-panel.js +9 -2
  123. package/build-module/components/global-styles/advanced-panel.js.map +1 -1
  124. package/build-module/components/global-styles/background-panel.js +430 -0
  125. package/build-module/components/global-styles/background-panel.js.map +1 -0
  126. package/build-module/components/global-styles/color-panel.js +2 -1
  127. package/build-module/components/global-styles/color-panel.js.map +1 -1
  128. package/build-module/components/global-styles/get-global-styles-changes.js +3 -0
  129. package/build-module/components/global-styles/get-global-styles-changes.js.map +1 -1
  130. package/build-module/components/global-styles/hooks.js +1 -1
  131. package/build-module/components/global-styles/hooks.js.map +1 -1
  132. package/build-module/components/global-styles/index.js +1 -0
  133. package/build-module/components/global-styles/index.js.map +1 -1
  134. package/build-module/components/global-styles/use-global-styles-output.js +16 -15
  135. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  136. package/build-module/components/global-styles/utils.js +1 -0
  137. package/build-module/components/global-styles/utils.js.map +1 -1
  138. package/build-module/components/iframe/index.js +9 -4
  139. package/build-module/components/iframe/index.js.map +1 -1
  140. package/build-module/components/inserter/block-patterns-tab/index.js.map +1 -1
  141. package/build-module/components/inserter/block-patterns-tab/pattern-category-preview-panel.js +5 -0
  142. package/build-module/components/inserter/block-patterns-tab/pattern-category-preview-panel.js.map +1 -1
  143. package/build-module/components/inserter/library.js +2 -0
  144. package/build-module/components/inserter/library.js.map +1 -1
  145. package/build-module/components/inserter/menu.js +8 -2
  146. package/build-module/components/inserter/menu.js.map +1 -1
  147. package/build-module/components/inserter/search-items.js +33 -15
  148. package/build-module/components/inserter/search-items.js.map +1 -1
  149. package/build-module/components/keyboard-shortcuts/index.js +11 -0
  150. package/build-module/components/keyboard-shortcuts/index.js.map +1 -1
  151. package/build-module/components/list-view/block-select-button.js +16 -0
  152. package/build-module/components/list-view/block-select-button.js.map +1 -1
  153. package/build-module/components/list-view/block.js +1 -1
  154. package/build-module/components/list-view/block.js.map +1 -1
  155. package/build-module/components/list-view/index.js +17 -2
  156. package/build-module/components/list-view/index.js.map +1 -1
  157. package/build-module/components/list-view/use-list-view-collapse-items.js +40 -0
  158. package/build-module/components/list-view/use-list-view-collapse-items.js.map +1 -0
  159. package/build-module/components/rich-text/index.js +15 -12
  160. package/build-module/components/rich-text/index.js.map +1 -1
  161. package/build-module/components/rich-text/index.native.js +16 -11
  162. package/build-module/components/rich-text/index.native.js.map +1 -1
  163. package/build-module/components/rich-text/native/get-format-colors.native.js +1 -1
  164. package/build-module/components/rich-text/native/get-format-colors.native.js.map +1 -1
  165. package/build-module/components/rich-text/native/index.native.js +2 -2
  166. package/build-module/components/rich-text/native/index.native.js.map +1 -1
  167. package/build-module/components/rich-text/with-deprecations.js +0 -3
  168. package/build-module/components/rich-text/with-deprecations.js.map +1 -1
  169. package/build-module/components/url-popover/image-url-input-ui.js +50 -36
  170. package/build-module/components/url-popover/image-url-input-ui.js.map +1 -1
  171. package/build-module/components/use-block-display-information/index.js +5 -7
  172. package/build-module/components/use-block-display-information/index.js.map +1 -1
  173. package/build-module/hooks/anchor.js +2 -2
  174. package/build-module/hooks/anchor.js.map +1 -1
  175. package/build-module/hooks/background.js +67 -419
  176. package/build-module/hooks/background.js.map +1 -1
  177. package/build-module/hooks/index.js +1 -0
  178. package/build-module/hooks/index.js.map +1 -1
  179. package/build-module/hooks/use-zoom-out.js +41 -0
  180. package/build-module/hooks/use-zoom-out.js.map +1 -0
  181. package/build-module/index.js +1 -1
  182. package/build-module/index.js.map +1 -1
  183. package/build-module/private-apis.js +7 -2
  184. package/build-module/private-apis.js.map +1 -1
  185. package/build-module/private-apis.native.js +3 -1
  186. package/build-module/private-apis.native.js.map +1 -1
  187. package/build-module/store/private-actions.js +12 -0
  188. package/build-module/store/private-actions.js.map +1 -1
  189. package/build-module/store/private-keys.js +1 -0
  190. package/build-module/store/private-keys.js.map +1 -1
  191. package/build-module/store/private-selectors.js +22 -4
  192. package/build-module/store/private-selectors.js.map +1 -1
  193. package/build-module/store/reducer.js +21 -0
  194. package/build-module/store/reducer.js.map +1 -1
  195. package/build-module/store/selectors.js +35 -33
  196. package/build-module/store/selectors.js.map +1 -1
  197. package/build-module/store/utils.js +6 -1
  198. package/build-module/store/utils.js.map +1 -1
  199. package/build-module/utils/transform-styles/index.js +2 -1
  200. package/build-module/utils/transform-styles/index.js.map +1 -1
  201. package/build-style/content-rtl.css +4 -1
  202. package/build-style/content.css +4 -1
  203. package/build-style/style-rtl.css +84 -79
  204. package/build-style/style.css +84 -79
  205. package/package.json +31 -31
  206. package/src/components/block-list/block.js +19 -3
  207. package/src/components/block-mover/button.js +4 -1
  208. package/src/components/block-mover/index.js +8 -1
  209. package/src/components/block-patterns-list/index.js +22 -17
  210. package/src/components/block-preview/style.scss +28 -0
  211. package/src/components/block-settings-menu/block-settings-dropdown.js +8 -2
  212. package/src/components/block-switcher/index.js +5 -3
  213. package/src/components/block-switcher/style.scss +1 -1
  214. package/src/components/block-toolbar/index.js +22 -19
  215. package/src/components/block-toolbar/shuffle.js +19 -13
  216. package/src/components/block-toolbar/style.scss +1 -1
  217. package/src/components/block-tools/block-selection-button.js +66 -9
  218. package/src/components/block-tools/index.js +18 -1
  219. package/src/components/button-block-appender/content.scss +5 -1
  220. package/src/components/default-block-appender/content.scss +2 -2
  221. package/src/components/global-styles/advanced-panel.js +8 -2
  222. package/src/components/global-styles/background-panel.js +591 -0
  223. package/src/components/global-styles/color-panel.js +2 -1
  224. package/src/components/global-styles/get-global-styles-changes.js +3 -0
  225. package/src/components/global-styles/hooks.js +1 -0
  226. package/src/components/global-styles/index.js +4 -0
  227. package/src/components/global-styles/style.scss +78 -1
  228. package/src/{hooks/test/background.js → components/global-styles/test/background-panel.js} +36 -1
  229. package/src/components/global-styles/test/get-global-styles-changes.js +22 -3
  230. package/src/components/global-styles/test/use-global-styles-output.js +9 -9
  231. package/src/components/global-styles/use-global-styles-output.js +27 -16
  232. package/src/components/global-styles/utils.js +1 -0
  233. package/src/components/iframe/index.js +19 -9
  234. package/src/components/inserter/block-patterns-tab/index.js +1 -0
  235. package/src/components/inserter/block-patterns-tab/pattern-category-preview-panel.js +5 -0
  236. package/src/components/inserter/library.js +4 -0
  237. package/src/components/inserter/menu.js +8 -1
  238. package/src/components/inserter/search-items.js +37 -15
  239. package/src/components/inserter/style.scss +6 -12
  240. package/src/components/keyboard-shortcuts/index.js +11 -0
  241. package/src/components/list-view/block-select-button.js +13 -1
  242. package/src/components/list-view/block.js +1 -1
  243. package/src/components/list-view/index.js +18 -1
  244. package/src/components/list-view/style.scss +4 -4
  245. package/src/components/list-view/use-list-view-collapse-items.js +33 -0
  246. package/src/components/rich-text/index.js +30 -13
  247. package/src/components/rich-text/index.native.js +14 -11
  248. package/src/components/rich-text/native/get-format-colors.native.js +1 -1
  249. package/src/components/rich-text/native/index.native.js +2 -2
  250. package/src/components/rich-text/with-deprecations.js +0 -3
  251. package/src/components/url-popover/image-url-input-ui.js +68 -51
  252. package/src/components/use-block-display-information/index.js +8 -10
  253. package/src/hooks/anchor.js +11 -9
  254. package/src/hooks/background.js +77 -538
  255. package/src/hooks/index.js +1 -0
  256. package/src/hooks/use-zoom-out.js +36 -0
  257. package/src/index.js +1 -0
  258. package/src/private-apis.js +13 -1
  259. package/src/private-apis.native.js +2 -0
  260. package/src/store/private-actions.js +12 -0
  261. package/src/store/private-keys.js +1 -0
  262. package/src/store/private-selectors.js +54 -27
  263. package/src/store/reducer.js +22 -0
  264. package/src/store/selectors.js +195 -180
  265. package/src/store/test/private-actions.js +10 -0
  266. package/src/store/test/private-selectors.js +13 -0
  267. package/src/store/test/reducer.js +26 -0
  268. package/src/store/test/selectors.js +90 -199
  269. package/src/store/utils.js +13 -0
  270. package/src/style.scss +0 -2
  271. package/src/utils/transform-styles/index.js +2 -1
  272. package/src/hooks/anchor.scss +0 -4
  273. package/src/hooks/background.scss +0 -75
@@ -0,0 +1,591 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import classnames from 'classnames';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import {
10
+ __experimentalToolsPanel as ToolsPanel,
11
+ __experimentalToolsPanelItem as ToolsPanelItem,
12
+ ToggleControl,
13
+ __experimentalToggleGroupControl as ToggleGroupControl,
14
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
15
+ __experimentalUnitControl as UnitControl,
16
+ __experimentalVStack as VStack,
17
+ DropZone,
18
+ FlexItem,
19
+ FocalPointPicker,
20
+ MenuItem,
21
+ VisuallyHidden,
22
+ __experimentalItemGroup as ItemGroup,
23
+ __experimentalHStack as HStack,
24
+ __experimentalTruncate as Truncate,
25
+ } from '@wordpress/components';
26
+ import { __, sprintf } from '@wordpress/i18n';
27
+ import { store as noticesStore } from '@wordpress/notices';
28
+ import { getFilename } from '@wordpress/url';
29
+ import { useCallback, Platform, useRef } from '@wordpress/element';
30
+ import { useDispatch, useSelect } from '@wordpress/data';
31
+ import { focus } from '@wordpress/dom';
32
+ import { isBlobURL } from '@wordpress/blob';
33
+
34
+ /**
35
+ * Internal dependencies
36
+ */
37
+ import { TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
38
+ import { setImmutably } from '../../utils/object';
39
+ import MediaReplaceFlow from '../media-replace-flow';
40
+ import { store as blockEditorStore } from '../../store';
41
+
42
+ const IMAGE_BACKGROUND_TYPE = 'image';
43
+
44
+ /**
45
+ * Checks site settings to see if the background panel may be used.
46
+ * `settings.background.backgroundSize` exists also,
47
+ * but can only be used if settings?.background?.backgroundImage is `true`.
48
+ *
49
+ * @param {Object} settings Site settings
50
+ * @return {boolean} Whether site settings has activated background panel.
51
+ */
52
+ export function useHasBackgroundPanel( settings ) {
53
+ return Platform.OS === 'web' && settings?.background?.backgroundImage;
54
+ }
55
+
56
+ /**
57
+ * Checks if there is a current value in the background size block support
58
+ * attributes. Background size values include background size as well
59
+ * as background position.
60
+ *
61
+ * @param {Object} style Style attribute.
62
+ * @return {boolean} Whether the block has a background size value set.
63
+ */
64
+ export function hasBackgroundSizeValue( style ) {
65
+ return (
66
+ style?.background?.backgroundPosition !== undefined ||
67
+ style?.background?.backgroundSize !== undefined
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Checks if there is a current value in the background image block support
73
+ * attributes.
74
+ *
75
+ * @param {Object} style Style attribute.
76
+ * @return {boolean} Whether the block has a background image value set.
77
+ */
78
+ export function hasBackgroundImageValue( style ) {
79
+ return (
80
+ !! style?.background?.backgroundImage?.id ||
81
+ !! style?.background?.backgroundImage?.url
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Get the help text for the background size control.
87
+ *
88
+ * @param {string} value backgroundSize value.
89
+ * @return {string} Translated help text.
90
+ */
91
+ function backgroundSizeHelpText( value ) {
92
+ if ( value === 'cover' || value === undefined ) {
93
+ return __( 'Image covers the space evenly.' );
94
+ }
95
+ if ( value === 'contain' ) {
96
+ return __( 'Image is contained without distortion.' );
97
+ }
98
+ return __( 'Specify a fixed width.' );
99
+ }
100
+
101
+ /**
102
+ * Converts decimal x and y coords from FocalPointPicker to percentage-based values
103
+ * to use as backgroundPosition value.
104
+ *
105
+ * @param {{x?:number, y?:number}} value FocalPointPicker coords.
106
+ * @return {string} backgroundPosition value.
107
+ */
108
+ export const coordsToBackgroundPosition = ( value ) => {
109
+ if ( ! value || ( isNaN( value.x ) && isNaN( value.y ) ) ) {
110
+ return undefined;
111
+ }
112
+
113
+ const x = isNaN( value.x ) ? 0.5 : value.x;
114
+ const y = isNaN( value.y ) ? 0.5 : value.y;
115
+
116
+ return `${ x * 100 }% ${ y * 100 }%`;
117
+ };
118
+
119
+ /**
120
+ * Converts backgroundPosition value to x and y coords for FocalPointPicker.
121
+ *
122
+ * @param {string} value backgroundPosition value.
123
+ * @return {{x?:number, y?:number}} FocalPointPicker coords.
124
+ */
125
+ export const backgroundPositionToCoords = ( value ) => {
126
+ if ( ! value ) {
127
+ return { x: undefined, y: undefined };
128
+ }
129
+
130
+ let [ x, y ] = value.split( ' ' ).map( ( v ) => parseFloat( v ) / 100 );
131
+ x = isNaN( x ) ? undefined : x;
132
+ y = isNaN( y ) ? x : y;
133
+
134
+ return { x, y };
135
+ };
136
+
137
+ function InspectorImagePreview( { label, filename, url: imgUrl } ) {
138
+ const imgLabel = label || getFilename( imgUrl );
139
+ return (
140
+ <ItemGroup as="span">
141
+ <HStack justify="flex-start" as="span">
142
+ <span
143
+ className={ classnames(
144
+ 'block-editor-global-styles-background-panel__inspector-image-indicator-wrapper',
145
+ {
146
+ 'has-image': imgUrl,
147
+ }
148
+ ) }
149
+ aria-hidden
150
+ >
151
+ { imgUrl && (
152
+ <span
153
+ className="block-editor-global-styles-background-panel__inspector-image-indicator"
154
+ style={ {
155
+ backgroundImage: `url(${ imgUrl })`,
156
+ } }
157
+ />
158
+ ) }
159
+ </span>
160
+ <FlexItem as="span">
161
+ <Truncate
162
+ numberOfLines={ 1 }
163
+ className="block-editor-global-styles-background-panel__inspector-media-replace-title"
164
+ >
165
+ { imgLabel }
166
+ </Truncate>
167
+ <VisuallyHidden as="span">
168
+ { filename
169
+ ? sprintf(
170
+ /* translators: %s: file name */
171
+ __( 'Selected image: %s' ),
172
+ filename
173
+ )
174
+ : __( 'No image selected' ) }
175
+ </VisuallyHidden>
176
+ </FlexItem>
177
+ </HStack>
178
+ </ItemGroup>
179
+ );
180
+ }
181
+
182
+ function BackgroundImageToolsPanelItem( {
183
+ panelId,
184
+ isShownByDefault,
185
+ onChange,
186
+ style,
187
+ inheritedValue,
188
+ } ) {
189
+ const mediaUpload = useSelect(
190
+ ( select ) => select( blockEditorStore ).getSettings().mediaUpload,
191
+ []
192
+ );
193
+
194
+ const { id, title, url } = style?.background?.backgroundImage || {
195
+ ...inheritedValue?.background?.backgroundImage,
196
+ };
197
+
198
+ const replaceContainerRef = useRef();
199
+
200
+ const { createErrorNotice } = useDispatch( noticesStore );
201
+ const onUploadError = ( message ) => {
202
+ createErrorNotice( message, { type: 'snackbar' } );
203
+ };
204
+
205
+ const resetBackgroundImage = () =>
206
+ onChange(
207
+ setImmutably(
208
+ style,
209
+ [ 'background', 'backgroundImage' ],
210
+ undefined
211
+ )
212
+ );
213
+
214
+ const onSelectMedia = ( media ) => {
215
+ if ( ! media || ! media.url ) {
216
+ resetBackgroundImage();
217
+ return;
218
+ }
219
+
220
+ if ( isBlobURL( media.url ) ) {
221
+ return;
222
+ }
223
+
224
+ // For media selections originated from a file upload.
225
+ if (
226
+ ( media.media_type &&
227
+ media.media_type !== IMAGE_BACKGROUND_TYPE ) ||
228
+ ( ! media.media_type &&
229
+ media.type &&
230
+ media.type !== IMAGE_BACKGROUND_TYPE )
231
+ ) {
232
+ onUploadError(
233
+ __( 'Only images can be used as a background image.' )
234
+ );
235
+ return;
236
+ }
237
+
238
+ onChange(
239
+ setImmutably( style, [ 'background', 'backgroundImage' ], {
240
+ url: media.url,
241
+ id: media.id,
242
+ source: 'file',
243
+ title: media.title || undefined,
244
+ } )
245
+ );
246
+ };
247
+
248
+ const onFilesDrop = ( filesList ) => {
249
+ mediaUpload( {
250
+ allowedTypes: [ 'image' ],
251
+ filesList,
252
+ onFileChange( [ image ] ) {
253
+ if ( isBlobURL( image?.url ) ) {
254
+ return;
255
+ }
256
+ onSelectMedia( image );
257
+ },
258
+ onError: onUploadError,
259
+ } );
260
+ };
261
+
262
+ const resetAllFilter = useCallback( ( previousValue ) => {
263
+ return {
264
+ ...previousValue,
265
+ style: {
266
+ ...previousValue.style,
267
+ background: undefined,
268
+ },
269
+ };
270
+ }, [] );
271
+
272
+ const hasValue =
273
+ hasBackgroundImageValue( style ) ||
274
+ hasBackgroundImageValue( inheritedValue );
275
+
276
+ return (
277
+ <ToolsPanelItem
278
+ className="single-column"
279
+ hasValue={ () => hasValue }
280
+ label={ __( 'Background image' ) }
281
+ onDeselect={ resetBackgroundImage }
282
+ isShownByDefault={ isShownByDefault }
283
+ resetAllFilter={ resetAllFilter }
284
+ panelId={ panelId }
285
+ >
286
+ <div
287
+ className="block-editor-global-styles-background-panel__inspector-media-replace-container"
288
+ ref={ replaceContainerRef }
289
+ >
290
+ <MediaReplaceFlow
291
+ mediaId={ id }
292
+ mediaURL={ url }
293
+ allowedTypes={ [ IMAGE_BACKGROUND_TYPE ] }
294
+ accept="image/*"
295
+ onSelect={ onSelectMedia }
296
+ name={
297
+ <InspectorImagePreview
298
+ label={ __( 'Background image' ) }
299
+ filename={ title || __( 'Untitled' ) }
300
+ url={ url }
301
+ />
302
+ }
303
+ variant="secondary"
304
+ >
305
+ { hasValue && (
306
+ <MenuItem
307
+ onClick={ () => {
308
+ const [ toggleButton ] = focus.tabbable.find(
309
+ replaceContainerRef.current
310
+ );
311
+ // Focus the toggle button and close the dropdown menu.
312
+ // This ensures similar behaviour as to selecting an image, where the dropdown is
313
+ // closed and focus is redirected to the dropdown toggle button.
314
+ toggleButton?.focus();
315
+ toggleButton?.click();
316
+ resetBackgroundImage();
317
+ } }
318
+ >
319
+ { __( 'Reset ' ) }
320
+ </MenuItem>
321
+ ) }
322
+ </MediaReplaceFlow>
323
+ <DropZone
324
+ onFilesDrop={ onFilesDrop }
325
+ label={ __( 'Drop to upload' ) }
326
+ />
327
+ </div>
328
+ </ToolsPanelItem>
329
+ );
330
+ }
331
+
332
+ function BackgroundSizeToolsPanelItem( {
333
+ panelId,
334
+ isShownByDefault,
335
+ onChange,
336
+ style,
337
+ inheritedValue,
338
+ defaultValues,
339
+ } ) {
340
+ const sizeValue =
341
+ style?.background?.backgroundSize ||
342
+ inheritedValue?.background?.backgroundSize;
343
+ const repeatValue =
344
+ style?.background?.backgroundRepeat ||
345
+ inheritedValue?.background?.backgroundRepeat;
346
+ const imageValue =
347
+ style?.background?.backgroundImage?.url ||
348
+ inheritedValue?.background?.backgroundImage?.url;
349
+ const positionValue =
350
+ style?.background?.backgroundPosition ||
351
+ inheritedValue?.background?.backgroundPosition;
352
+
353
+ /*
354
+ * An `undefined` value is replaced with any supplied
355
+ * default control value for the toggle group control.
356
+ * An empty string is treated as `auto` - this allows a user
357
+ * to select "Size" and then enter a custom value, with an
358
+ * empty value being treated as `auto`.
359
+ */
360
+ const currentValueForToggle =
361
+ ( sizeValue !== undefined &&
362
+ sizeValue !== 'cover' &&
363
+ sizeValue !== 'contain' ) ||
364
+ sizeValue === ''
365
+ ? 'auto'
366
+ : sizeValue || defaultValues?.backgroundSize;
367
+
368
+ /*
369
+ * If the current value is `cover` and the repeat value is `undefined`, then
370
+ * the toggle should be unchecked as the default state. Otherwise, the toggle
371
+ * should reflect the current repeat value.
372
+ */
373
+ const repeatCheckedValue = ! (
374
+ repeatValue === 'no-repeat' ||
375
+ ( currentValueForToggle === 'cover' && repeatValue === undefined )
376
+ );
377
+
378
+ const hasValue = hasBackgroundSizeValue( style );
379
+
380
+ const resetAllFilter = useCallback( ( previousValue ) => {
381
+ return {
382
+ ...previousValue,
383
+ style: {
384
+ ...previousValue.style,
385
+ background: {
386
+ ...previousValue.style?.background,
387
+ backgroundRepeat: undefined,
388
+ backgroundSize: undefined,
389
+ },
390
+ },
391
+ };
392
+ }, [] );
393
+
394
+ const updateBackgroundSize = ( next ) => {
395
+ // When switching to 'contain' toggle the repeat off.
396
+ let nextRepeat = repeatValue;
397
+
398
+ if ( next === 'contain' ) {
399
+ nextRepeat = 'no-repeat';
400
+ }
401
+
402
+ if ( next === 'cover' ) {
403
+ nextRepeat = undefined;
404
+ }
405
+
406
+ if (
407
+ ( currentValueForToggle === 'cover' ||
408
+ currentValueForToggle === 'contain' ) &&
409
+ next === 'auto'
410
+ ) {
411
+ nextRepeat = undefined;
412
+ }
413
+
414
+ onChange(
415
+ setImmutably( style, [ 'background' ], {
416
+ ...style?.background,
417
+ backgroundRepeat: nextRepeat,
418
+ backgroundSize: next,
419
+ } )
420
+ );
421
+ };
422
+
423
+ const updateBackgroundPosition = ( next ) => {
424
+ onChange(
425
+ setImmutably(
426
+ style,
427
+ [ 'background', 'backgroundPosition' ],
428
+ coordsToBackgroundPosition( next )
429
+ )
430
+ );
431
+ };
432
+
433
+ const toggleIsRepeated = () =>
434
+ onChange(
435
+ setImmutably(
436
+ style,
437
+ [ 'background', 'backgroundRepeat' ],
438
+ repeatCheckedValue === true ? 'no-repeat' : undefined
439
+ )
440
+ );
441
+
442
+ const resetBackgroundSize = () =>
443
+ onChange(
444
+ setImmutably( style, [ 'background' ], {
445
+ ...style?.background,
446
+ backgroundPosition: undefined,
447
+ backgroundRepeat: undefined,
448
+ backgroundSize: undefined,
449
+ } )
450
+ );
451
+
452
+ return (
453
+ <VStack
454
+ as={ ToolsPanelItem }
455
+ spacing={ 2 }
456
+ className="single-column"
457
+ hasValue={ () => hasValue }
458
+ label={ __( 'Size' ) }
459
+ onDeselect={ resetBackgroundSize }
460
+ isShownByDefault={ isShownByDefault }
461
+ resetAllFilter={ resetAllFilter }
462
+ panelId={ panelId }
463
+ >
464
+ <FocalPointPicker
465
+ __next40pxDefaultSize
466
+ label={ __( 'Position' ) }
467
+ url={ imageValue }
468
+ value={ backgroundPositionToCoords( positionValue ) }
469
+ onChange={ updateBackgroundPosition }
470
+ />
471
+ <ToggleGroupControl
472
+ size={ '__unstable-large' }
473
+ label={ __( 'Size' ) }
474
+ value={ currentValueForToggle }
475
+ onChange={ updateBackgroundSize }
476
+ isBlock
477
+ help={ backgroundSizeHelpText( sizeValue ) }
478
+ >
479
+ <ToggleGroupControlOption
480
+ key={ 'cover' }
481
+ value={ 'cover' }
482
+ label={ __( 'Cover' ) }
483
+ />
484
+ <ToggleGroupControlOption
485
+ key={ 'contain' }
486
+ value={ 'contain' }
487
+ label={ __( 'Contain' ) }
488
+ />
489
+ <ToggleGroupControlOption
490
+ key={ 'fixed' }
491
+ value={ 'auto' }
492
+ label={ __( 'Fixed' ) }
493
+ />
494
+ </ToggleGroupControl>
495
+ { sizeValue !== undefined &&
496
+ sizeValue !== 'cover' &&
497
+ sizeValue !== 'contain' ? (
498
+ <UnitControl
499
+ size={ '__unstable-large' }
500
+ onChange={ updateBackgroundSize }
501
+ value={ sizeValue }
502
+ />
503
+ ) : null }
504
+ { currentValueForToggle !== 'cover' && (
505
+ <ToggleControl
506
+ label={ __( 'Repeat' ) }
507
+ checked={ repeatCheckedValue }
508
+ onChange={ toggleIsRepeated }
509
+ />
510
+ ) }
511
+ </VStack>
512
+ );
513
+ }
514
+
515
+ function BackgroundToolsPanel( {
516
+ resetAllFilter,
517
+ onChange,
518
+ value,
519
+ panelId,
520
+ children,
521
+ } ) {
522
+ const resetAll = () => {
523
+ const updatedValue = resetAllFilter( value );
524
+ onChange( updatedValue );
525
+ };
526
+
527
+ return (
528
+ <VStack
529
+ as={ ToolsPanel }
530
+ spacing={ 6 }
531
+ label={ __( 'Background' ) }
532
+ resetAll={ resetAll }
533
+ panelId={ panelId }
534
+ dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
535
+ >
536
+ { children }
537
+ </VStack>
538
+ );
539
+ }
540
+
541
+ const DEFAULT_CONTROLS = {
542
+ backgroundImage: true,
543
+ backgroundSize: true,
544
+ };
545
+
546
+ export default function BackgroundPanel( {
547
+ as: Wrapper = BackgroundToolsPanel,
548
+ value,
549
+ onChange,
550
+ inheritedValue = value,
551
+ settings,
552
+ panelId,
553
+ defaultControls = DEFAULT_CONTROLS,
554
+ defaultValues = {},
555
+ } ) {
556
+ const resetAllFilter = useCallback( ( previousValue ) => {
557
+ return {
558
+ ...previousValue,
559
+ background: {},
560
+ };
561
+ }, [] );
562
+ const shouldShowBackgroundSizeControls =
563
+ settings?.background?.backgroundSize;
564
+
565
+ return (
566
+ <Wrapper
567
+ resetAllFilter={ resetAllFilter }
568
+ value={ value }
569
+ onChange={ onChange }
570
+ panelId={ panelId }
571
+ >
572
+ <BackgroundImageToolsPanelItem
573
+ onChange={ onChange }
574
+ panelId={ panelId }
575
+ isShownByDefault={ defaultControls.backgroundImage }
576
+ style={ value }
577
+ inheritedValue={ inheritedValue }
578
+ />
579
+ { shouldShowBackgroundSizeControls && (
580
+ <BackgroundSizeToolsPanelItem
581
+ onChange={ onChange }
582
+ panelId={ panelId }
583
+ isShownByDefault={ defaultControls.backgroundSize }
584
+ style={ value }
585
+ inheritedValue={ inheritedValue }
586
+ defaultValues={ defaultValues }
587
+ />
588
+ ) }
589
+ </Wrapper>
590
+ );
591
+ }
@@ -123,10 +123,11 @@ function ColorToolsPanel( {
123
123
 
124
124
  return (
125
125
  <ToolsPanel
126
- label={ __( 'Color' ) }
126
+ label={ __( 'Elements' ) }
127
127
  resetAll={ resetAll }
128
128
  panelId={ panelId }
129
129
  hasInnerWrapper
130
+ headingLevel={ 3 }
130
131
  className="color-block-support-panel"
131
132
  __experimentalFirstVisibleItemClass="first"
132
133
  __experimentalLastVisibleItemClass="last"
@@ -26,6 +26,7 @@ const translationMap = {
26
26
  'settings.typography': __( 'Typography' ),
27
27
  'styles.color': __( 'Colors' ),
28
28
  'styles.spacing': __( 'Spacing' ),
29
+ 'styles.background': __( 'Background' ),
29
30
  'styles.typography': __( 'Typography' ),
30
31
  };
31
32
  const getBlockNames = memoize( () =>
@@ -126,6 +127,7 @@ export function getGlobalStylesChangelist( next, previous ) {
126
127
  const changedValueTree = deepCompare(
127
128
  {
128
129
  styles: {
130
+ background: next?.styles?.background,
129
131
  color: next?.styles?.color,
130
132
  typography: next?.styles?.typography,
131
133
  spacing: next?.styles?.spacing,
@@ -136,6 +138,7 @@ export function getGlobalStylesChangelist( next, previous ) {
136
138
  },
137
139
  {
138
140
  styles: {
141
+ background: previous?.styles?.background,
139
142
  color: previous?.styles?.color,
140
143
  typography: previous?.styles?.typography,
141
144
  spacing: previous?.styles?.spacing,
@@ -27,6 +27,7 @@ const VALID_SETTINGS = [
27
27
  'background.backgroundImage',
28
28
  'background.backgroundRepeat',
29
29
  'background.backgroundSize',
30
+ 'background.backgroundPosition',
30
31
  'border.color',
31
32
  'border.radius',
32
33
  'border.style',
@@ -31,5 +31,9 @@ export {
31
31
  useHasImageSettingsPanel,
32
32
  } from './image-settings-panel';
33
33
  export { default as AdvancedPanel } from './advanced-panel';
34
+ export {
35
+ default as BackgroundPanel,
36
+ useHasBackgroundPanel,
37
+ } from './background-panel';
34
38
  export { areGlobalStyleConfigsEqual } from './utils';
35
39
  export { default as getGlobalStylesChanges } from './get-global-styles-changes';