@wordpress/block-editor 15.15.0 → 15.16.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 (94) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-inspector/index.cjs +2 -1
  3. package/build/components/block-inspector/index.cjs.map +2 -2
  4. package/build/components/block-visibility/modal.cjs +2 -2
  5. package/build/components/block-visibility/modal.cjs.map +1 -1
  6. package/build/components/block-visibility/viewport-visibility-info.cjs +6 -1
  7. package/build/components/block-visibility/viewport-visibility-info.cjs.map +2 -2
  8. package/build/components/global-styles/background-panel.cjs +142 -33
  9. package/build/components/global-styles/background-panel.cjs.map +2 -2
  10. package/build/components/global-styles/color-panel.cjs +18 -7
  11. package/build/components/global-styles/color-panel.cjs.map +2 -2
  12. package/build/components/global-styles/hooks.cjs +8 -4
  13. package/build/components/global-styles/hooks.cjs.map +2 -2
  14. package/build/components/inspector-controls-tabs/styles-tab.cjs +2 -1
  15. package/build/components/inspector-controls-tabs/styles-tab.cjs.map +2 -2
  16. package/build/hooks/background.cjs +74 -21
  17. package/build/hooks/background.cjs.map +3 -3
  18. package/build/hooks/cross-origin-isolation.cjs +6 -6
  19. package/build/hooks/cross-origin-isolation.cjs.map +2 -2
  20. package/build/hooks/custom-css.cjs +5 -0
  21. package/build/hooks/custom-css.cjs.map +2 -2
  22. package/build/hooks/fit-text.cjs +46 -58
  23. package/build/hooks/fit-text.cjs.map +3 -3
  24. package/build/hooks/utils.cjs +5 -1
  25. package/build/hooks/utils.cjs.map +2 -2
  26. package/build/store/actions.cjs +8 -4
  27. package/build/store/actions.cjs.map +2 -2
  28. package/build/store/private-selectors.cjs +2 -2
  29. package/build/store/private-selectors.cjs.map +2 -2
  30. package/build/store/reducer.cjs +62 -95
  31. package/build/store/reducer.cjs.map +2 -2
  32. package/build/store/selectors.cjs +2 -2
  33. package/build/store/selectors.cjs.map +2 -2
  34. package/build-module/components/block-inspector/index.mjs +2 -1
  35. package/build-module/components/block-inspector/index.mjs.map +2 -2
  36. package/build-module/components/block-visibility/modal.mjs +2 -2
  37. package/build-module/components/block-visibility/modal.mjs.map +1 -1
  38. package/build-module/components/block-visibility/viewport-visibility-info.mjs +6 -1
  39. package/build-module/components/block-visibility/viewport-visibility-info.mjs.map +2 -2
  40. package/build-module/components/global-styles/background-panel.mjs +141 -34
  41. package/build-module/components/global-styles/background-panel.mjs.map +2 -2
  42. package/build-module/components/global-styles/color-panel.mjs +17 -7
  43. package/build-module/components/global-styles/color-panel.mjs.map +2 -2
  44. package/build-module/components/global-styles/hooks.mjs +8 -4
  45. package/build-module/components/global-styles/hooks.mjs.map +2 -2
  46. package/build-module/components/inspector-controls-tabs/styles-tab.mjs +2 -1
  47. package/build-module/components/inspector-controls-tabs/styles-tab.mjs.map +2 -2
  48. package/build-module/hooks/background.mjs +76 -22
  49. package/build-module/hooks/background.mjs.map +2 -2
  50. package/build-module/hooks/cross-origin-isolation.mjs +6 -6
  51. package/build-module/hooks/cross-origin-isolation.mjs.map +2 -2
  52. package/build-module/hooks/custom-css.mjs +5 -0
  53. package/build-module/hooks/custom-css.mjs.map +2 -2
  54. package/build-module/hooks/fit-text.mjs +46 -58
  55. package/build-module/hooks/fit-text.mjs.map +2 -2
  56. package/build-module/hooks/utils.mjs +5 -1
  57. package/build-module/hooks/utils.mjs.map +2 -2
  58. package/build-module/store/actions.mjs +8 -4
  59. package/build-module/store/actions.mjs.map +2 -2
  60. package/build-module/store/private-selectors.mjs +2 -2
  61. package/build-module/store/private-selectors.mjs.map +2 -2
  62. package/build-module/store/reducer.mjs +62 -94
  63. package/build-module/store/reducer.mjs.map +2 -2
  64. package/build-module/store/selectors.mjs +2 -2
  65. package/build-module/store/selectors.mjs.map +2 -2
  66. package/build-style/content-rtl.css +2 -2
  67. package/build-style/content.css +2 -2
  68. package/build-style/style-rtl.css +35 -7
  69. package/build-style/style.css +35 -7
  70. package/package.json +39 -39
  71. package/src/components/background-image-control/style.scss +0 -4
  72. package/src/components/block-inspector/index.js +1 -0
  73. package/src/components/block-visibility/viewport-visibility-info.js +8 -1
  74. package/src/components/fit-text-size-warning/style.scss +1 -5
  75. package/src/components/global-styles/background-panel.js +157 -11
  76. package/src/components/global-styles/color-panel.js +23 -7
  77. package/src/components/global-styles/hooks.js +12 -4
  78. package/src/components/global-styles/test/background-panel.js +44 -1
  79. package/src/components/inspector-controls-tabs/styles-tab.js +1 -0
  80. package/src/hooks/background.js +122 -21
  81. package/src/hooks/background.scss +45 -0
  82. package/src/hooks/cross-origin-isolation.js +6 -6
  83. package/src/hooks/custom-css.js +6 -0
  84. package/src/hooks/fit-text.js +73 -83
  85. package/src/hooks/test/cross-origin-isolation.js +7 -3
  86. package/src/hooks/utils.js +4 -0
  87. package/src/store/actions.js +9 -8
  88. package/src/store/private-selectors.js +2 -2
  89. package/src/store/reducer.js +84 -128
  90. package/src/store/selectors.js +2 -2
  91. package/src/store/test/private-selectors.js +67 -34
  92. package/src/store/test/reducer.js +436 -66
  93. package/src/store/test/selectors.js +81 -63
  94. package/src/style.scss +1 -0
@@ -199,7 +199,7 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection=true] .blo
199
199
  outline-style: solid;
200
200
  outline-width: calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
201
201
  outline-offset: calc(1 * -1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
202
- box-shadow: inset 0 0 0 calc(var(--wp-admin-border-width-focus, 2px) + 1px) #fff, 0 0 0 1px #fff, 0 1px 1px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.02), 0 3px 3px rgba(0, 0, 0, 0.02), 0 4px 4px rgba(0, 0, 0, 0.01);
202
+ box-shadow: inset 0 0 0 calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1) + 0.5px) rgba(255, 255, 255, 0.7);
203
203
  z-index: 1;
204
204
  }
205
205
  .block-editor-block-list__layout .block-editor-block-list__block.is-block-hidden {
@@ -294,7 +294,7 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection=true] .blo
294
294
  outline-style: solid;
295
295
  outline-width: calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
296
296
  outline-offset: calc(1 * -1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
297
- box-shadow: inset 0 0 0 calc(var(--wp-admin-border-width-focus, 2px) + 1px) #fff, 0 0 0 1px #fff, 0 1px 1px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.02), 0 3px 3px rgba(0, 0, 0, 0.02), 0 4px 4px rgba(0, 0, 0, 0.01);
297
+ box-shadow: inset 0 0 0 calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1) + 0.5px) rgba(255, 255, 255, 0.7);
298
298
  }
299
299
 
300
300
  .is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.is-hovered::after, .is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.is-selected::after, .is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.is-highlighted::after,
@@ -199,7 +199,7 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection=true] .blo
199
199
  outline-style: solid;
200
200
  outline-width: calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
201
201
  outline-offset: calc(1 * -1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
202
- box-shadow: inset 0 0 0 calc(var(--wp-admin-border-width-focus, 2px) + 1px) #fff, 0 0 0 1px #fff, 0 1px 1px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.02), 0 3px 3px rgba(0, 0, 0, 0.02), 0 4px 4px rgba(0, 0, 0, 0.01);
202
+ box-shadow: inset 0 0 0 calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1) + 0.5px) rgba(255, 255, 255, 0.7);
203
203
  z-index: 1;
204
204
  }
205
205
  .block-editor-block-list__layout .block-editor-block-list__block.is-block-hidden {
@@ -294,7 +294,7 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection=true] .blo
294
294
  outline-style: solid;
295
295
  outline-width: calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
296
296
  outline-offset: calc(1 * -1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1));
297
- box-shadow: inset 0 0 0 calc(var(--wp-admin-border-width-focus, 2px) + 1px) #fff, 0 0 0 1px #fff, 0 1px 1px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.02), 0 3px 3px rgba(0, 0, 0, 0.02), 0 4px 4px rgba(0, 0, 0, 0.01);
297
+ box-shadow: inset 0 0 0 calc(1 * var(--wp-admin-border-width-focus) / var(--wp-block-editor-iframe-zoom-out-scale, 1) + 0.5px) rgba(255, 255, 255, 0.7);
298
298
  }
299
299
 
300
300
  .is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.is-hovered::after, .is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.is-selected::after, .is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.is-highlighted::after,
@@ -124,9 +124,6 @@
124
124
  }
125
125
 
126
126
  .block-editor-global-styles-background-panel__inspector-media-replace-container {
127
- border: 1px solid #ddd;
128
- border-radius: 2px;
129
- grid-column: 1/-1;
130
127
  position: relative;
131
128
  }
132
129
  .block-editor-global-styles-background-panel__inspector-media-replace-container.is-open {
@@ -1622,10 +1619,7 @@ iframe[name=editor-canvas] {
1622
1619
  }
1623
1620
 
1624
1621
  .block-editor-fit-text-size-warning {
1625
- margin: 8px 0 0 0;
1626
- }
1627
- .block-editor-fit-text-size-warning .components-notice__content {
1628
- margin-left: 0;
1622
+ grid-column: span 2;
1629
1623
  }
1630
1624
 
1631
1625
  .components-font-appearance-control [role=option] {
@@ -3410,6 +3404,40 @@ iframe[name=editor-canvas] {
3410
3404
  font-size: 12px;
3411
3405
  }
3412
3406
 
3407
+ /**
3408
+ * ItemGroup-like border styles for the background panel's ToolsPanelItems.
3409
+ * Replicates the separated border of the `ItemGroup` component while
3410
+ * allowing for hidden placeholder items (same approach as the color panel).
3411
+ */
3412
+ .block-editor-background-panel__item {
3413
+ padding: 0;
3414
+ max-width: 100%;
3415
+ position: relative;
3416
+ border-right: 1px solid #ddd;
3417
+ border-left: 1px solid #ddd;
3418
+ border-bottom: 1px solid #ddd;
3419
+ }
3420
+ .block-editor-background-panel__item:nth-child(1 of .block-editor-background-panel__item) {
3421
+ border-top-right-radius: 2px;
3422
+ border-top-left-radius: 2px;
3423
+ border-top: 1px solid #ddd;
3424
+ }
3425
+ .block-editor-background-panel__item:nth-last-child(1 of .block-editor-background-panel__item) {
3426
+ border-bottom-right-radius: 2px;
3427
+ border-bottom-left-radius: 2px;
3428
+ }
3429
+ .block-editor-background-panel__item > div,
3430
+ .block-editor-background-panel__item > div > button {
3431
+ border-radius: inherit;
3432
+ }
3433
+
3434
+ .background-block-support-panel {
3435
+ /* Increased specificity required to remove the slot wrapper's row gap */
3436
+ }
3437
+ .background-block-support-panel.background-block-support-panel .background-block-support-panel__inner-wrapper {
3438
+ row-gap: 0;
3439
+ }
3440
+
3413
3441
  .block-editor-content-only-controls__link {
3414
3442
  width: 100%;
3415
3443
  box-shadow: inset 0 0 0 1px #ccc;
@@ -124,9 +124,6 @@
124
124
  }
125
125
 
126
126
  .block-editor-global-styles-background-panel__inspector-media-replace-container {
127
- border: 1px solid #ddd;
128
- border-radius: 2px;
129
- grid-column: 1/-1;
130
127
  position: relative;
131
128
  }
132
129
  .block-editor-global-styles-background-panel__inspector-media-replace-container.is-open {
@@ -1622,10 +1619,7 @@ iframe[name=editor-canvas] {
1622
1619
  }
1623
1620
 
1624
1621
  .block-editor-fit-text-size-warning {
1625
- margin: 8px 0 0 0;
1626
- }
1627
- .block-editor-fit-text-size-warning .components-notice__content {
1628
- margin-right: 0;
1622
+ grid-column: span 2;
1629
1623
  }
1630
1624
 
1631
1625
  .components-font-appearance-control [role=option] {
@@ -3412,6 +3406,40 @@ iframe[name=editor-canvas] {
3412
3406
  font-size: 12px;
3413
3407
  }
3414
3408
 
3409
+ /**
3410
+ * ItemGroup-like border styles for the background panel's ToolsPanelItems.
3411
+ * Replicates the separated border of the `ItemGroup` component while
3412
+ * allowing for hidden placeholder items (same approach as the color panel).
3413
+ */
3414
+ .block-editor-background-panel__item {
3415
+ padding: 0;
3416
+ max-width: 100%;
3417
+ position: relative;
3418
+ border-left: 1px solid #ddd;
3419
+ border-right: 1px solid #ddd;
3420
+ border-bottom: 1px solid #ddd;
3421
+ }
3422
+ .block-editor-background-panel__item:nth-child(1 of .block-editor-background-panel__item) {
3423
+ border-top-left-radius: 2px;
3424
+ border-top-right-radius: 2px;
3425
+ border-top: 1px solid #ddd;
3426
+ }
3427
+ .block-editor-background-panel__item:nth-last-child(1 of .block-editor-background-panel__item) {
3428
+ border-bottom-left-radius: 2px;
3429
+ border-bottom-right-radius: 2px;
3430
+ }
3431
+ .block-editor-background-panel__item > div,
3432
+ .block-editor-background-panel__item > div > button {
3433
+ border-radius: inherit;
3434
+ }
3435
+
3436
+ .background-block-support-panel {
3437
+ /* Increased specificity required to remove the slot wrapper's row gap */
3438
+ }
3439
+ .background-block-support-panel.background-block-support-panel .background-block-support-panel__inner-wrapper {
3440
+ row-gap: 0;
3441
+ }
3442
+
3415
3443
  .block-editor-content-only-controls__link {
3416
3444
  width: 100%;
3417
3445
  box-shadow: inset 0 0 0 1px #ccc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "15.15.0",
3
+ "version": "15.16.0",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -61,43 +61,43 @@
61
61
  ],
62
62
  "dependencies": {
63
63
  "@react-spring/web": "^9.4.5",
64
- "@wordpress/a11y": "^4.42.0",
65
- "@wordpress/api-fetch": "^7.42.0",
66
- "@wordpress/base-styles": "^6.18.0",
67
- "@wordpress/blob": "^4.42.0",
68
- "@wordpress/block-serialization-default-parser": "^5.42.0",
69
- "@wordpress/blocks": "^15.15.0",
70
- "@wordpress/commands": "^1.42.0",
71
- "@wordpress/components": "^32.4.0",
72
- "@wordpress/compose": "^7.42.0",
73
- "@wordpress/data": "^10.42.0",
74
- "@wordpress/dataviews": "^13.1.0",
75
- "@wordpress/date": "^5.42.0",
76
- "@wordpress/deprecated": "^4.42.0",
77
- "@wordpress/dom": "^4.42.0",
78
- "@wordpress/element": "^6.42.0",
79
- "@wordpress/escape-html": "^3.42.0",
80
- "@wordpress/global-styles-engine": "^1.9.0",
81
- "@wordpress/hooks": "^4.42.0",
82
- "@wordpress/html-entities": "^4.42.0",
83
- "@wordpress/i18n": "^6.15.0",
84
- "@wordpress/icons": "^12.0.0",
85
- "@wordpress/image-cropper": "^1.6.0",
86
- "@wordpress/interactivity": "^6.42.0",
87
- "@wordpress/is-shallow-equal": "^5.42.0",
88
- "@wordpress/keyboard-shortcuts": "^5.42.0",
89
- "@wordpress/keycodes": "^4.42.0",
90
- "@wordpress/notices": "^5.42.0",
91
- "@wordpress/preferences": "^4.42.0",
92
- "@wordpress/priority-queue": "^3.42.0",
93
- "@wordpress/private-apis": "^1.42.0",
94
- "@wordpress/rich-text": "^7.42.0",
95
- "@wordpress/style-engine": "^2.42.0",
96
- "@wordpress/token-list": "^3.42.0",
97
- "@wordpress/upload-media": "^0.27.0",
98
- "@wordpress/url": "^4.42.0",
99
- "@wordpress/warning": "^3.42.0",
100
- "@wordpress/wordcount": "^4.42.0",
64
+ "@wordpress/a11y": "^4.43.0",
65
+ "@wordpress/api-fetch": "^7.43.0",
66
+ "@wordpress/base-styles": "^6.19.0",
67
+ "@wordpress/blob": "^4.43.0",
68
+ "@wordpress/block-serialization-default-parser": "^5.43.0",
69
+ "@wordpress/blocks": "^15.16.0",
70
+ "@wordpress/commands": "^1.43.0",
71
+ "@wordpress/components": "^32.5.0",
72
+ "@wordpress/compose": "^7.43.0",
73
+ "@wordpress/data": "^10.43.0",
74
+ "@wordpress/dataviews": "^14.0.0",
75
+ "@wordpress/date": "^5.43.0",
76
+ "@wordpress/deprecated": "^4.43.0",
77
+ "@wordpress/dom": "^4.43.0",
78
+ "@wordpress/element": "^6.43.0",
79
+ "@wordpress/escape-html": "^3.43.0",
80
+ "@wordpress/global-styles-engine": "^1.10.0",
81
+ "@wordpress/hooks": "^4.43.0",
82
+ "@wordpress/html-entities": "^4.43.0",
83
+ "@wordpress/i18n": "^6.16.0",
84
+ "@wordpress/icons": "^12.1.0",
85
+ "@wordpress/image-cropper": "^1.7.0",
86
+ "@wordpress/interactivity": "^6.43.0",
87
+ "@wordpress/is-shallow-equal": "^5.43.0",
88
+ "@wordpress/keyboard-shortcuts": "^5.43.0",
89
+ "@wordpress/keycodes": "^4.43.0",
90
+ "@wordpress/notices": "^5.43.0",
91
+ "@wordpress/preferences": "^4.43.0",
92
+ "@wordpress/priority-queue": "^3.43.0",
93
+ "@wordpress/private-apis": "^1.43.0",
94
+ "@wordpress/rich-text": "^7.43.0",
95
+ "@wordpress/style-engine": "^2.43.0",
96
+ "@wordpress/token-list": "^3.43.0",
97
+ "@wordpress/upload-media": "^0.28.0",
98
+ "@wordpress/url": "^4.43.0",
99
+ "@wordpress/warning": "^3.43.0",
100
+ "@wordpress/wordcount": "^4.43.0",
101
101
  "change-case": "^4.1.2",
102
102
  "clsx": "^2.1.1",
103
103
  "colord": "^2.7.0",
@@ -124,5 +124,5 @@
124
124
  "publishConfig": {
125
125
  "access": "public"
126
126
  },
127
- "gitHead": "c20787b1778ae64c2db65643b1c236309d68e6ba"
127
+ "gitHead": "2cea90674d11aa521ec3f71652fb3a6a4c383969"
128
128
  }
@@ -3,10 +3,6 @@
3
3
  @use "@wordpress/base-styles/z-index" as *;
4
4
 
5
5
  .block-editor-global-styles-background-panel__inspector-media-replace-container {
6
- border: $border-width solid $gray-300;
7
- border-radius: $radius-small;
8
- // Full width. ToolsPanel lays out children in a grid.
9
- grid-column: 1 / -1;
10
6
  position: relative;
11
7
 
12
8
  &.is-open {
@@ -54,6 +54,7 @@ function StyleInspectorSlots( {
54
54
  <InspectorControls.Slot
55
55
  group="background"
56
56
  label={ __( 'Background image' ) }
57
+ className="background-block-support-panel__inner-wrapper"
57
58
  />
58
59
  <InspectorControls.Slot
59
60
  group="typography"
@@ -17,6 +17,7 @@ import { unseen } from '@wordpress/icons';
17
17
  import { unlock } from '../../lock-unlock';
18
18
  import { store as blockEditorStore } from '../../store';
19
19
  import useBlockVisibility from './use-block-visibility';
20
+ import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
20
21
  import { deviceTypeKey } from '../../store/private-keys';
21
22
  import { BLOCK_VISIBILITY_VIEWPORTS } from './constants';
22
23
 
@@ -56,10 +57,16 @@ export default function ViewportVisibilityInfo( { clientId } ) {
56
57
  [ clientId ]
57
58
  );
58
59
 
59
- // Use hook to get current viewport and if block is currently hidden (accurate viewport detection)
60
+ // Get the block's DOM element to derive the canvas iframe window,
61
+ // so viewport detection matches the actual block rendering context.
62
+ const blockElement = useBlockElement( clientId );
63
+ const rawCanvasView = blockElement?.ownerDocument?.defaultView;
64
+ const canvasView = rawCanvasView === null ? undefined : rawCanvasView;
65
+
60
66
  const { isBlockCurrentlyHidden, currentViewport } = useBlockVisibility( {
61
67
  blockVisibility: currentBlockVisibility,
62
68
  deviceType: selectedDeviceType,
69
+ view: canvasView,
63
70
  } );
64
71
 
65
72
  /*
@@ -1,9 +1,5 @@
1
1
  @use "@wordpress/base-styles/variables" as *;
2
2
 
3
3
  .block-editor-fit-text-size-warning {
4
- margin: $grid-unit-10 0 0 0;
5
-
6
- .components-notice__content {
7
- margin-right: 0;
8
- }
4
+ grid-column: span 2;
9
5
  }
@@ -7,17 +7,33 @@ import {
7
7
  } from '@wordpress/components';
8
8
  import { useCallback, Platform } from '@wordpress/element';
9
9
  import { __ } from '@wordpress/i18n';
10
+ import { getValueFromVariable } from '@wordpress/global-styles-engine';
11
+
10
12
  /**
11
13
  * Internal dependencies
12
14
  */
13
15
  import BackgroundImageControl from '../background-image-control';
16
+ import { ColorPanelDropdown } from './color-panel';
17
+ import { useGradientsPerOrigin } from './hooks';
14
18
  import { useToolsPanelDropdownMenuProps } from './utils';
15
19
  import { setImmutably } from '../../utils/object';
16
20
 
17
21
  const DEFAULT_CONTROLS = {
18
22
  backgroundImage: true,
23
+ gradient: true,
19
24
  };
20
25
 
26
+ /**
27
+ * Checks site settings to see if the requested feature's control may be used.
28
+ *
29
+ * @param {Object} settings Site settings.
30
+ * @param {string} feature Background feature to check.
31
+ * @return {boolean} Whether site settings has activated background panel.
32
+ */
33
+ export function useHasBackgroundControl( settings, feature ) {
34
+ return Platform.OS === 'web' && settings?.background?.[ feature ];
35
+ }
36
+
21
37
  /**
22
38
  * Checks site settings to see if the background panel may be used.
23
39
  * `settings.background.backgroundSize` exists also,
@@ -27,7 +43,8 @@ const DEFAULT_CONTROLS = {
27
43
  * @return {boolean} Whether site settings has activated background panel.
28
44
  */
29
45
  export function useHasBackgroundPanel( settings ) {
30
- return Platform.OS === 'web' && settings?.background?.backgroundImage;
46
+ const { backgroundImage, gradient } = settings?.background || {};
47
+ return Platform.OS === 'web' && ( backgroundImage || gradient );
31
48
  }
32
49
 
33
50
  /**
@@ -61,6 +78,20 @@ export function hasBackgroundImageValue( style ) {
61
78
  );
62
79
  }
63
80
 
81
+ /**
82
+ * Checks if there is a current value in the background gradient block support
83
+ * attributes.
84
+ *
85
+ * @param {Object} style Style attribute.
86
+ * @return {boolean} Whether the block has a background gradient value set.
87
+ */
88
+ export function hasBackgroundGradientValue( style ) {
89
+ return (
90
+ 'string' === typeof style?.background?.gradient &&
91
+ style?.background?.gradient !== ''
92
+ );
93
+ }
94
+
64
95
  function BackgroundToolsPanel( {
65
96
  resetAllFilter,
66
97
  onChange,
@@ -80,9 +111,15 @@ function BackgroundToolsPanel( {
80
111
  label={ headerLabel }
81
112
  resetAll={ resetAll }
82
113
  panelId={ panelId }
114
+ hasInnerWrapper
115
+ className="background-block-support-panel"
116
+ __experimentalFirstVisibleItemClass="first"
117
+ __experimentalLastVisibleItemClass="last"
83
118
  dropdownMenuProps={ dropdownMenuProps }
84
119
  >
85
- { children }
120
+ <div className="background-block-support-panel__inner-wrapper">
121
+ { children }
122
+ </div>
86
123
  </ToolsPanel>
87
124
  );
88
125
  }
@@ -98,15 +135,97 @@ export default function BackgroundImagePanel( {
98
135
  defaultValues = {},
99
136
  headerLabel = __( 'Background' ),
100
137
  } ) {
101
- const showBackgroundImageControl = useHasBackgroundPanel( settings );
138
+ const gradients = useGradientsPerOrigin( settings );
139
+ const areCustomGradientsEnabled = settings?.color?.customGradient;
140
+ const hasGradientColors = gradients.length > 0 || areCustomGradientsEnabled;
141
+
142
+ const hasBackgroundGradientControl = useHasBackgroundControl(
143
+ settings,
144
+ 'gradient'
145
+ );
146
+ const showBackgroundGradientControl =
147
+ hasGradientColors && hasBackgroundGradientControl;
148
+ const showBackgroundImageControl = useHasBackgroundControl(
149
+ settings,
150
+ 'backgroundImage'
151
+ );
152
+
153
+ const resetAllFilter = useCallback(
154
+ ( previousValue ) => {
155
+ return {
156
+ ...previousValue,
157
+ background: {},
158
+ color: hasBackgroundGradientControl
159
+ ? {
160
+ ...previousValue?.color,
161
+ gradient: undefined,
162
+ }
163
+ : previousValue?.color,
164
+ };
165
+ },
166
+ [ hasBackgroundGradientControl ]
167
+ );
168
+
169
+ if ( ! showBackgroundGradientControl && ! showBackgroundImageControl ) {
170
+ return null;
171
+ }
172
+
173
+ const decodeValue = ( rawValue ) =>
174
+ getValueFromVariable( { settings }, '', rawValue );
175
+ const encodeGradientValue = ( gradientValue ) => {
176
+ const allGradients = gradients.flatMap(
177
+ ( { gradients: originGradients } ) => originGradients
178
+ );
179
+ const gradientObject = allGradients.find(
180
+ ( { gradient } ) => gradient === gradientValue
181
+ );
182
+ return gradientObject
183
+ ? 'var:preset|gradient|' + gradientObject.slug
184
+ : gradientValue;
185
+ };
186
+
102
187
  const resetBackground = () =>
103
- onChange( setImmutably( value, [ 'background' ], {} ) );
104
- const resetAllFilter = useCallback( ( previousValue ) => {
105
- return {
106
- ...previousValue,
107
- background: {},
108
- };
109
- }, [] );
188
+ onChange(
189
+ setImmutably(
190
+ value,
191
+ [ 'background', 'backgroundImage' ],
192
+ undefined
193
+ )
194
+ );
195
+
196
+ const resetGradient = () => {
197
+ let newValue = setImmutably(
198
+ value,
199
+ [ 'background', 'gradient' ],
200
+ undefined
201
+ );
202
+ newValue = setImmutably( newValue, [ 'color', 'gradient' ], undefined );
203
+ onChange( newValue );
204
+ };
205
+
206
+ // Get current gradient value, decoding preset slug references.
207
+ // Fall back to color.gradient for legacy blocks that haven't migrated
208
+ // to background.gradient yet (mirrors block inspector fallback in
209
+ // packages/block-editor/src/hooks/background.js).
210
+ const currentGradient = decodeValue(
211
+ value?.background?.gradient ?? value?.color?.gradient
212
+ );
213
+ const inheritedGradient = decodeValue(
214
+ inheritedValue?.background?.gradient ?? inheritedValue?.color?.gradient
215
+ );
216
+
217
+ // Set gradient value, encoding preset matches as slug references.
218
+ // Also clear color.gradient to migrate from the legacy location,
219
+ // matching the block inspector behavior in hooks/background.js.
220
+ const setGradient = ( newGradient ) => {
221
+ let newValue = setImmutably(
222
+ value,
223
+ [ 'background', 'gradient' ],
224
+ encodeGradientValue( newGradient )
225
+ );
226
+ newValue = setImmutably( newValue, [ 'color', 'gradient' ], undefined );
227
+ onChange( newValue );
228
+ };
110
229
 
111
230
  return (
112
231
  <Wrapper
@@ -118,7 +237,8 @@ export default function BackgroundImagePanel( {
118
237
  >
119
238
  { showBackgroundImageControl && (
120
239
  <ToolsPanelItem
121
- hasValue={ () => !! value?.background }
240
+ className="block-editor-background-panel__item"
241
+ hasValue={ () => hasBackgroundImageValue( value ) }
122
242
  label={ __( 'Image' ) }
123
243
  onDeselect={ resetBackground }
124
244
  isShownByDefault={ defaultControls.backgroundImage }
@@ -134,6 +254,32 @@ export default function BackgroundImagePanel( {
134
254
  />
135
255
  </ToolsPanelItem>
136
256
  ) }
257
+ { showBackgroundGradientControl && (
258
+ <ColorPanelDropdown
259
+ className="block-editor-background-panel__item"
260
+ label={ __( 'Gradient' ) }
261
+ hasValue={ () => hasBackgroundGradientValue( value ) }
262
+ resetValue={ resetGradient }
263
+ isShownByDefault={ defaultControls.gradient }
264
+ indicators={ [ currentGradient ] }
265
+ tabs={ [
266
+ {
267
+ key: 'gradient',
268
+ label: __( 'Gradient' ),
269
+ inheritedValue:
270
+ currentGradient ?? inheritedGradient,
271
+ setValue: setGradient,
272
+ userValue: currentGradient,
273
+ isGradient: true,
274
+ },
275
+ ] }
276
+ colorGradientControlSettings={ {
277
+ gradients,
278
+ disableCustomGradients: ! areCustomGradientsEnabled,
279
+ } }
280
+ panelId={ panelId }
281
+ />
282
+ ) }
137
283
  </Wrapper>
138
284
  );
139
285
  }
@@ -201,7 +201,7 @@ function ColorPanelTab( {
201
201
  );
202
202
  }
203
203
 
204
- function ColorPanelDropdown( {
204
+ export function ColorPanelDropdown( {
205
205
  label,
206
206
  hasValue,
207
207
  resetValue,
@@ -210,13 +210,14 @@ function ColorPanelDropdown( {
210
210
  tabs,
211
211
  colorGradientControlSettings,
212
212
  panelId,
213
+ className = 'block-editor-tools-panel-color-gradient-settings__item',
213
214
  } ) {
214
215
  const currentTab = tabs.find( ( tab ) => tab.userValue !== undefined );
215
216
  const { key: firstTabKey, ...firstTab } = tabs[ 0 ] ?? {};
216
217
  const colorGradientDropdownButtonRef = useRef( undefined );
217
218
  return (
218
219
  <ToolsPanelItem
219
- className="block-editor-tools-panel-color-gradient-settings__item"
220
+ className={ className }
220
221
  hasValue={ hasValue }
221
222
  label={ label }
222
223
  onDeselect={ resetValue }
@@ -336,6 +337,12 @@ export default function ColorPanel( {
336
337
  const areCustomGradientsEnabled = settings?.color?.customGradient;
337
338
  const hasSolidColors = colors.length > 0 || areCustomSolidsEnabled;
338
339
  const hasGradientColors = gradients.length > 0 || areCustomGradientsEnabled;
340
+ // When a block opts into background.gradient support, the gradient
341
+ // picker moves to the Background panel. Hide it here to avoid
342
+ // showing duplicate gradient controls.
343
+ const hasBackgroundGradientSupport = !! settings?.background?.gradient;
344
+ const showGradientColors =
345
+ hasGradientColors && ! hasBackgroundGradientSupport;
339
346
  const decodeValue = ( rawValue ) =>
340
347
  getValueFromVariable( { settings }, '', rawValue );
341
348
  const encodeColorValue = ( colorValue ) => {
@@ -367,14 +374,18 @@ export default function ColorPanel( {
367
374
  const userBackgroundColor = decodeValue( value?.color?.background );
368
375
  const gradient = decodeValue( inheritedValue?.color?.gradient );
369
376
  const userGradient = decodeValue( value?.color?.gradient );
370
- const hasBackground = () => !! userBackgroundColor || !! userGradient;
377
+ const hasBackground = () =>
378
+ !! userBackgroundColor ||
379
+ ( ! hasBackgroundGradientSupport && !! userGradient );
371
380
  const setBackgroundColor = ( newColor ) => {
372
381
  const newValue = setImmutably(
373
382
  value,
374
383
  [ 'color', 'background' ],
375
384
  encodeColorValue( newColor )
376
385
  );
377
- newValue.color.gradient = undefined;
386
+ if ( ! hasBackgroundGradientSupport ) {
387
+ newValue.color.gradient = undefined;
388
+ }
378
389
  onChange( newValue );
379
390
  };
380
391
  const setGradient = ( newGradient ) => {
@@ -392,7 +403,9 @@ export default function ColorPanel( {
392
403
  [ 'color', 'background' ],
393
404
  undefined
394
405
  );
395
- newValue.color.gradient = undefined;
406
+ if ( ! hasBackgroundGradientSupport ) {
407
+ newValue.color.gradient = undefined;
408
+ }
396
409
  onChange( newValue );
397
410
  };
398
411
 
@@ -566,7 +579,10 @@ export default function ColorPanel( {
566
579
  hasValue: hasBackground,
567
580
  resetValue: resetBackground,
568
581
  isShownByDefault: defaultControls.background,
569
- indicators: [ gradient ?? backgroundColor ],
582
+ indicators: [
583
+ ( showGradientColors ? gradient : undefined ) ??
584
+ backgroundColor,
585
+ ],
570
586
  tabs: [
571
587
  hasSolidColors && {
572
588
  key: 'background',
@@ -575,7 +591,7 @@ export default function ColorPanel( {
575
591
  setValue: setBackgroundColor,
576
592
  userValue: userBackgroundColor,
577
593
  },
578
- hasGradientColors && {
594
+ showGradientColors && {
579
595
  key: 'gradient',
580
596
  label: __( 'Gradient' ),
581
597
  inheritedValue: gradient,
@@ -81,7 +81,11 @@ export function useSettingsForBlockElement(
81
81
  };
82
82
 
83
83
  // Some blocks can enable background colors but disable gradients.
84
- if ( ! supportedStyles.includes( 'background' ) ) {
84
+ // Preserve gradient settings when background.gradient is supported.
85
+ if (
86
+ ! supportedStyles.includes( 'background' ) &&
87
+ ! supportedStyles.includes( 'backgroundGradient' )
88
+ ) {
85
89
  updatedSettings.color.gradients = [];
86
90
  updatedSettings.color.customGradient = false;
87
91
  }
@@ -184,11 +188,15 @@ export function useSettingsForBlockElement(
184
188
  }
185
189
  } );
186
190
 
187
- [ 'backgroundImage', 'backgroundSize' ].forEach( ( key ) => {
188
- if ( ! supportedStyles.includes( key ) ) {
191
+ [
192
+ [ 'backgroundImage', 'backgroundImage' ],
193
+ [ 'backgroundSize', 'backgroundSize' ],
194
+ [ 'backgroundGradient', 'gradient' ],
195
+ ].forEach( ( [ styleKey, settingKey ] ) => {
196
+ if ( ! supportedStyles.includes( styleKey ) ) {
189
197
  updatedSettings.background = {
190
198
  ...updatedSettings.background,
191
- [ key ]: false,
199
+ [ settingKey ]: false,
192
200
  };
193
201
  }
194
202
  } );