@wordpress/block-editor 15.13.2-next.v.202602241322.0 → 15.13.2

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 (175) hide show
  1. package/build/components/block-bindings/attribute-control.cjs +5 -2
  2. package/build/components/block-bindings/attribute-control.cjs.map +2 -2
  3. package/build/components/block-inspector/edit-contents.cjs +5 -4
  4. package/build/components/block-inspector/edit-contents.cjs.map +2 -2
  5. package/build/components/block-list/block-html.cjs +6 -2
  6. package/build/components/block-list/block-html.cjs.map +2 -2
  7. package/build/components/block-list/block.cjs +9 -1
  8. package/build/components/block-list/block.cjs.map +2 -2
  9. package/build/components/block-list/use-block-props/index.cjs +10 -1
  10. package/build/components/block-list/use-block-props/index.cjs.map +2 -2
  11. package/build/components/block-settings-menu-controls/index.cjs +1 -0
  12. package/build/components/block-settings-menu-controls/index.cjs.map +2 -2
  13. package/build/components/block-styles/index.cjs +7 -1
  14. package/build/components/block-styles/index.cjs.map +3 -3
  15. package/build/components/block-switcher/block-transformations-menu.cjs +2 -0
  16. package/build/components/block-switcher/block-transformations-menu.cjs.map +2 -2
  17. package/build/components/block-switcher/block-variation-transformations.cjs +2 -0
  18. package/build/components/block-switcher/block-variation-transformations.cjs.map +2 -2
  19. package/build/components/block-toolbar/block-toolbar-icon.cjs +1 -1
  20. package/build/components/block-toolbar/block-toolbar-icon.cjs.map +2 -2
  21. package/build/components/block-toolbar/index.cjs +8 -4
  22. package/build/components/block-toolbar/index.cjs.map +2 -2
  23. package/build/components/block-tools/index.cjs +4 -3
  24. package/build/components/block-tools/index.cjs.map +2 -2
  25. package/build/components/block-variation-transforms/index.cjs +11 -5
  26. package/build/components/block-variation-transforms/index.cjs.map +2 -2
  27. package/build/components/block-visibility/modal.cjs +2 -2
  28. package/build/components/block-visibility/modal.cjs.map +1 -1
  29. package/build/components/block-visibility/use-block-visibility.cjs +4 -3
  30. package/build/components/block-visibility/use-block-visibility.cjs.map +2 -2
  31. package/build/components/block-visibility/viewport-visibility-info.cjs +6 -1
  32. package/build/components/block-visibility/viewport-visibility-info.cjs.map +2 -2
  33. package/build/components/dimensions-tool/scale-tool.cjs +2 -2
  34. package/build/components/dimensions-tool/scale-tool.cjs.map +2 -2
  35. package/build/components/iframe/index.cjs +1 -3
  36. package/build/components/iframe/index.cjs.map +2 -2
  37. package/build/components/iframe/use-scale-canvas.cjs +0 -1
  38. package/build/components/iframe/use-scale-canvas.cjs.map +2 -2
  39. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.cjs +8 -4
  40. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.cjs.map +2 -2
  41. package/build/components/link-picker/link-preview.cjs +2 -1
  42. package/build/components/link-picker/link-preview.cjs.map +3 -3
  43. package/build/components/provider/index.cjs +23 -7
  44. package/build/components/provider/index.cjs.map +2 -2
  45. package/build/components/provider/use-media-upload-settings.cjs +1 -0
  46. package/build/components/provider/use-media-upload-settings.cjs.map +2 -2
  47. package/build/components/writing-flow/use-arrow-nav.cjs +1 -1
  48. package/build/components/writing-flow/use-arrow-nav.cjs.map +2 -2
  49. package/build/hooks/auto-inspector-controls.cjs +26 -2
  50. package/build/hooks/auto-inspector-controls.cjs.map +3 -3
  51. package/build/hooks/cross-origin-isolation.cjs +9 -75
  52. package/build/hooks/cross-origin-isolation.cjs.map +2 -2
  53. package/build/hooks/custom-css.cjs +26 -0
  54. package/build/hooks/custom-css.cjs.map +3 -3
  55. package/build/private-apis.cjs +1 -0
  56. package/build/private-apis.cjs.map +2 -2
  57. package/build/store/actions.cjs +0 -8
  58. package/build/store/actions.cjs.map +2 -2
  59. package/build/store/private-keys.cjs +3 -0
  60. package/build/store/private-keys.cjs.map +2 -2
  61. package/build/store/private-selectors.cjs +4 -3
  62. package/build/store/private-selectors.cjs.map +2 -2
  63. package/build/store/reducer.cjs +78 -74
  64. package/build/store/reducer.cjs.map +2 -2
  65. package/build/store/selectors.cjs +16 -14
  66. package/build/store/selectors.cjs.map +2 -2
  67. package/build-module/components/block-bindings/attribute-control.mjs +5 -2
  68. package/build-module/components/block-bindings/attribute-control.mjs.map +2 -2
  69. package/build-module/components/block-inspector/edit-contents.mjs +5 -4
  70. package/build-module/components/block-inspector/edit-contents.mjs.map +2 -2
  71. package/build-module/components/block-list/block-html.mjs +7 -3
  72. package/build-module/components/block-list/block-html.mjs.map +2 -2
  73. package/build-module/components/block-list/block.mjs +10 -2
  74. package/build-module/components/block-list/block.mjs.map +2 -2
  75. package/build-module/components/block-list/use-block-props/index.mjs +11 -2
  76. package/build-module/components/block-list/use-block-props/index.mjs.map +2 -2
  77. package/build-module/components/block-settings-menu-controls/index.mjs +1 -0
  78. package/build-module/components/block-settings-menu-controls/index.mjs.map +2 -2
  79. package/build-module/components/block-styles/index.mjs +7 -1
  80. package/build-module/components/block-styles/index.mjs.map +2 -2
  81. package/build-module/components/block-switcher/block-transformations-menu.mjs +2 -0
  82. package/build-module/components/block-switcher/block-transformations-menu.mjs.map +2 -2
  83. package/build-module/components/block-switcher/block-variation-transformations.mjs +2 -0
  84. package/build-module/components/block-switcher/block-variation-transformations.mjs.map +2 -2
  85. package/build-module/components/block-toolbar/block-toolbar-icon.mjs +1 -1
  86. package/build-module/components/block-toolbar/block-toolbar-icon.mjs.map +2 -2
  87. package/build-module/components/block-toolbar/index.mjs +8 -4
  88. package/build-module/components/block-toolbar/index.mjs.map +2 -2
  89. package/build-module/components/block-tools/index.mjs +4 -3
  90. package/build-module/components/block-tools/index.mjs.map +2 -2
  91. package/build-module/components/block-variation-transforms/index.mjs +11 -5
  92. package/build-module/components/block-variation-transforms/index.mjs.map +2 -2
  93. package/build-module/components/block-visibility/modal.mjs +2 -2
  94. package/build-module/components/block-visibility/modal.mjs.map +1 -1
  95. package/build-module/components/block-visibility/use-block-visibility.mjs +4 -3
  96. package/build-module/components/block-visibility/use-block-visibility.mjs.map +2 -2
  97. package/build-module/components/block-visibility/viewport-visibility-info.mjs +6 -1
  98. package/build-module/components/block-visibility/viewport-visibility-info.mjs.map +2 -2
  99. package/build-module/components/dimensions-tool/scale-tool.mjs +2 -2
  100. package/build-module/components/dimensions-tool/scale-tool.mjs.map +2 -2
  101. package/build-module/components/iframe/index.mjs +2 -9
  102. package/build-module/components/iframe/index.mjs.map +2 -2
  103. package/build-module/components/iframe/use-scale-canvas.mjs +0 -1
  104. package/build-module/components/iframe/use-scale-canvas.mjs.map +2 -2
  105. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.mjs +8 -4
  106. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.mjs.map +2 -2
  107. package/build-module/components/link-picker/link-preview.mjs +2 -1
  108. package/build-module/components/link-picker/link-preview.mjs.map +2 -2
  109. package/build-module/components/provider/index.mjs +23 -7
  110. package/build-module/components/provider/index.mjs.map +2 -2
  111. package/build-module/components/provider/use-media-upload-settings.mjs +1 -0
  112. package/build-module/components/provider/use-media-upload-settings.mjs.map +2 -2
  113. package/build-module/components/writing-flow/use-arrow-nav.mjs +1 -1
  114. package/build-module/components/writing-flow/use-arrow-nav.mjs.map +2 -2
  115. package/build-module/hooks/auto-inspector-controls.mjs +28 -4
  116. package/build-module/hooks/auto-inspector-controls.mjs.map +2 -2
  117. package/build-module/hooks/cross-origin-isolation.mjs +9 -75
  118. package/build-module/hooks/cross-origin-isolation.mjs.map +2 -2
  119. package/build-module/hooks/custom-css.mjs +28 -2
  120. package/build-module/hooks/custom-css.mjs.map +2 -2
  121. package/build-module/private-apis.mjs +3 -1
  122. package/build-module/private-apis.mjs.map +2 -2
  123. package/build-module/store/actions.mjs +0 -8
  124. package/build-module/store/actions.mjs.map +2 -2
  125. package/build-module/store/private-keys.mjs +2 -0
  126. package/build-module/store/private-keys.mjs.map +2 -2
  127. package/build-module/store/private-selectors.mjs +4 -3
  128. package/build-module/store/private-selectors.mjs.map +2 -2
  129. package/build-module/store/reducer.mjs +78 -73
  130. package/build-module/store/reducer.mjs.map +2 -2
  131. package/build-module/store/selectors.mjs +16 -14
  132. package/build-module/store/selectors.mjs.map +2 -2
  133. package/build-style/content-rtl.css +25 -0
  134. package/build-style/content.css +25 -0
  135. package/build-style/style-rtl.css +3 -0
  136. package/build-style/style.css +3 -0
  137. package/package.json +39 -39
  138. package/src/components/block-bindings/attribute-control.js +8 -3
  139. package/src/components/block-inspector/edit-contents.js +5 -3
  140. package/src/components/block-list/block-html.js +13 -3
  141. package/src/components/block-list/block.js +10 -1
  142. package/src/components/block-list/content.scss +35 -0
  143. package/src/components/block-list/use-block-props/index.js +11 -1
  144. package/src/components/block-settings-menu-controls/index.js +1 -0
  145. package/src/components/block-styles/index.js +7 -1
  146. package/src/components/block-switcher/block-transformations-menu.js +2 -0
  147. package/src/components/block-switcher/block-variation-transformations.js +2 -0
  148. package/src/components/block-toolbar/block-toolbar-icon.js +6 -1
  149. package/src/components/block-toolbar/index.js +7 -2
  150. package/src/components/block-tools/index.js +11 -4
  151. package/src/components/block-tools/style.scss +5 -0
  152. package/src/components/block-variation-transforms/index.js +41 -36
  153. package/src/components/block-visibility/use-block-visibility.js +4 -2
  154. package/src/components/block-visibility/viewport-visibility-info.js +8 -1
  155. package/src/components/dimensions-tool/scale-tool.js +2 -2
  156. package/src/components/iframe/index.js +2 -12
  157. package/src/components/iframe/use-scale-canvas.js +0 -1
  158. package/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js +9 -5
  159. package/src/components/link-picker/link-preview.js +2 -1
  160. package/src/components/provider/index.js +49 -6
  161. package/src/components/provider/use-media-upload-settings.js +1 -0
  162. package/src/components/writing-flow/use-arrow-nav.js +9 -2
  163. package/src/hooks/auto-inspector-controls.js +31 -4
  164. package/src/hooks/cross-origin-isolation.js +10 -109
  165. package/src/hooks/custom-css.js +36 -2
  166. package/src/hooks/test/cross-origin-isolation.js +15 -42
  167. package/src/private-apis.js +2 -0
  168. package/src/store/actions.js +0 -8
  169. package/src/store/private-keys.js +1 -0
  170. package/src/store/private-selectors.js +6 -3
  171. package/src/store/reducer.js +119 -97
  172. package/src/store/selectors.js +59 -22
  173. package/src/store/test/private-selectors.js +67 -34
  174. package/src/store/test/reducer.js +545 -57
  175. package/src/store/test/selectors.js +611 -53
@@ -146,41 +146,46 @@ function VariationsToggleGroupControl( {
146
146
 
147
147
  function __experimentalBlockVariationTransforms( { blockClientId } ) {
148
148
  const { updateBlockAttributes } = useDispatch( blockEditorStore );
149
- const { activeBlockVariation, variations, isContentOnly, isSection } =
150
- useSelect(
151
- ( select ) => {
152
- const { getActiveBlockVariation, getBlockVariations } =
153
- select( blocksStore );
154
-
155
- const {
156
- getBlockName,
157
- getBlockAttributes,
158
- getBlockEditingMode,
159
- isSectionBlock,
160
- } = unlock( select( blockEditorStore ) );
161
-
162
- const name = blockClientId && getBlockName( blockClientId );
163
-
164
- const { hasContentRoleAttribute } = unlock(
165
- select( blocksStore )
166
- );
167
- const isContentBlock = hasContentRoleAttribute( name );
168
-
169
- return {
170
- activeBlockVariation: getActiveBlockVariation(
171
- name,
172
- getBlockAttributes( blockClientId ),
173
- 'transform'
174
- ),
175
- variations: name && getBlockVariations( name, 'transform' ),
176
- isContentOnly:
177
- getBlockEditingMode( blockClientId ) ===
178
- 'contentOnly' && ! isContentBlock,
179
- isSection: isSectionBlock( blockClientId ),
180
- };
181
- },
182
- [ blockClientId ]
183
- );
149
+ const {
150
+ activeBlockVariation,
151
+ variations,
152
+ canEdit,
153
+ isContentOnly,
154
+ isSection,
155
+ } = useSelect(
156
+ ( select ) => {
157
+ const { getActiveBlockVariation, getBlockVariations } =
158
+ select( blocksStore );
159
+
160
+ const {
161
+ getBlockName,
162
+ getBlockAttributes,
163
+ getBlockEditingMode,
164
+ isSectionBlock,
165
+ } = unlock( select( blockEditorStore ) );
166
+ const { canEditBlock } = select( blockEditorStore );
167
+
168
+ const name = blockClientId && getBlockName( blockClientId );
169
+
170
+ const { hasContentRoleAttribute } = unlock( select( blocksStore ) );
171
+ const isContentBlock = hasContentRoleAttribute( name );
172
+
173
+ return {
174
+ activeBlockVariation: getActiveBlockVariation(
175
+ name,
176
+ getBlockAttributes( blockClientId ),
177
+ 'transform'
178
+ ),
179
+ variations: name && getBlockVariations( name, 'transform' ),
180
+ canEdit: canEditBlock( blockClientId ),
181
+ isContentOnly:
182
+ getBlockEditingMode( blockClientId ) === 'contentOnly' &&
183
+ ! isContentBlock,
184
+ isSection: isSectionBlock( blockClientId ),
185
+ };
186
+ },
187
+ [ blockClientId ]
188
+ );
184
189
 
185
190
  const selectedValue = activeBlockVariation?.name;
186
191
 
@@ -205,7 +210,7 @@ function __experimentalBlockVariationTransforms( { blockClientId } ) {
205
210
  } );
206
211
  };
207
212
 
208
- if ( ! variations?.length || isContentOnly || isSection ) {
213
+ if ( ! variations?.length || ! canEdit || isContentOnly || isSection ) {
209
214
  return null;
210
215
  }
211
216
 
@@ -14,16 +14,18 @@ import { BLOCK_VISIBILITY_VIEWPORTS } from './constants';
14
14
  * @param {Object} options Parameters to avoid extra store subscriptions.
15
15
  * @param {Object|boolean} options.blockVisibility Block visibility metadata.
16
16
  * @param {string} options.deviceType Current device type ('desktop', 'tablet', 'mobile').
17
+ * @param {Window?} options.view Window instance in which to perform viewport matching
17
18
  * @return {Object} Object with `isBlockCurrentlyHidden` (boolean) and `currentViewport` (string) properties.
18
19
  */
19
20
  export default function useBlockVisibility( options = {} ) {
20
21
  const {
21
22
  blockVisibility = undefined,
22
23
  deviceType = BLOCK_VISIBILITY_VIEWPORTS.desktop.key,
24
+ view = window,
23
25
  } = options;
24
26
 
25
- const isLargerThanMobile = useViewportMatch( 'mobile', '>=' ); // >= 480px
26
- const isLargerThanTablet = useViewportMatch( 'medium', '>=' ); // >= 782px
27
+ const isLargerThanMobile = useViewportMatch( 'mobile', '>=', view ); // >= 480px
28
+ const isLargerThanTablet = useViewportMatch( 'medium', '>=', view ); // >= 782px
27
29
 
28
30
  /*
29
31
  * Priority:
@@ -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
  /*
@@ -98,14 +98,14 @@ export default function ScaleTool( {
98
98
 
99
99
  return (
100
100
  <ToolsPanelItem
101
- label={ __( 'Scale' ) }
101
+ label={ _x( 'Scale', 'Image scaling options' ) }
102
102
  isShownByDefault={ isShownByDefault }
103
103
  hasValue={ () => displayValue !== defaultValue }
104
104
  onDeselect={ () => onChange( defaultValue ) }
105
105
  panelId={ panelId }
106
106
  >
107
107
  <ToggleGroupControl
108
- label={ __( 'Scale' ) }
108
+ label={ _x( 'Scale', 'Image scaling options' ) }
109
109
  isBlock
110
110
  help={ scaleHelp[ displayValue ] }
111
111
  value={ displayValue }
@@ -8,12 +8,7 @@ import clsx from 'clsx';
8
8
  */
9
9
  import { useState, createPortal, forwardRef } from '@wordpress/element';
10
10
  import { __ } from '@wordpress/i18n';
11
- import {
12
- useMergeRefs,
13
- useRefEffect,
14
- useDisabled,
15
- useViewportMatch,
16
- } from '@wordpress/compose';
11
+ import { useMergeRefs, useRefEffect, useDisabled } from '@wordpress/compose';
17
12
  import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components';
18
13
  import { useSelect } from '@wordpress/data';
19
14
 
@@ -25,8 +20,6 @@ import { getCompatibilityStyles } from './get-compatibility-styles';
25
20
  import { useScaleCanvas } from './use-scale-canvas';
26
21
  import { store as blockEditorStore } from '../../store';
27
22
 
28
- const ViewportWidthProvider = useViewportMatch.__experimentalWidthProvider;
29
-
30
23
  function bubbleEvent( event, Constructor, frame ) {
31
24
  const init = {};
32
25
 
@@ -274,7 +267,6 @@ function Iframe( {
274
267
  const {
275
268
  contentResizeListener,
276
269
  containerResizeListener,
277
- containerWidth,
278
270
  isZoomedOut,
279
271
  scaleContainerWidth,
280
272
  } = useScaleCanvas( {
@@ -355,9 +347,7 @@ function Iframe( {
355
347
  >
356
348
  { contentResizeListener }
357
349
  <StyleProvider document={ iframeDocument }>
358
- <ViewportWidthProvider value={ containerWidth }>
359
- { children }
360
- </ViewportWidthProvider>
350
+ { children }
361
351
  </StyleProvider>
362
352
  </body>,
363
353
  iframeDocument.documentElement
@@ -484,7 +484,6 @@ export function useScaleCanvas( {
484
484
  return {
485
485
  isZoomedOut,
486
486
  scaleContainerWidth,
487
- containerWidth,
488
487
  contentResizeListener,
489
488
  containerResizeListener,
490
489
  };
@@ -111,14 +111,18 @@ export default function useInspectorControlsTabs(
111
111
  tabs.push( TAB_SETTINGS );
112
112
  }
113
113
 
114
- if ( hasBlockStyles || hasStyleFills ) {
114
+ const { tabSettings, isPreviewMode } = useSelect( ( select ) => {
115
+ const settings = select( blockEditorStore ).getSettings();
116
+ return {
117
+ tabSettings: settings.blockInspectorTabs,
118
+ isPreviewMode: settings.isPreviewMode,
119
+ };
120
+ }, [] );
121
+
122
+ if ( ! isPreviewMode && ( hasBlockStyles || hasStyleFills ) ) {
115
123
  tabs.push( TAB_STYLES );
116
124
  }
117
125
 
118
- const tabSettings = useSelect( ( select ) => {
119
- return select( blockEditorStore ).getSettings().blockInspectorTabs;
120
- }, [] );
121
-
122
126
  const showTabs = getShowTabs( blockName, tabSettings );
123
127
  return showTabs ? tabs : EMPTY_ARRAY;
124
128
  }
@@ -9,6 +9,7 @@ import {
9
9
  privateApis as componentsPrivateApis,
10
10
  } from '@wordpress/components';
11
11
  import { Icon, chevronDown } from '@wordpress/icons';
12
+ import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
12
13
 
13
14
  /**
14
15
  * Internal dependencies
@@ -50,7 +51,7 @@ export function LinkPreview( { title, url, image, badges } ) {
50
51
  numberOfLines={ 1 }
51
52
  className="link-preview-button__title"
52
53
  >
53
- { title }
54
+ { stripHTML( title ) }
54
55
  </Truncate>
55
56
  { url && (
56
57
  <Truncate
@@ -20,6 +20,7 @@ import { BlockRefsProvider } from './block-refs-provider';
20
20
  import { unlock } from '../../lock-unlock';
21
21
  import KeyboardShortcuts from '../keyboard-shortcuts';
22
22
  import useMediaUploadSettings from './use-media-upload-settings';
23
+ import { mediaUploadOnSuccessKey } from '../../store/private-keys';
23
24
  import { SelectionContext } from './selection-context';
24
25
 
25
26
  /** @typedef {import('@wordpress/data').WPDataRegistry} WPDataRegistry */
@@ -89,6 +90,7 @@ function shouldEnableClientSideMediaProcessing() {
89
90
  * or when adding a file to the editor via drag & drop.
90
91
  *
91
92
  * @param {WPDataRegistry} registry
93
+ * @param {Object} settings Block editor settings.
92
94
  * @param {Object} $3 Parameters object passed to the function.
93
95
  * @param {Array} $3.allowedTypes Array with the types of media that can be uploaded, if unset all types are allowed.
94
96
  * @param {Object} $3.additionalData Additional data to include in the request.
@@ -100,6 +102,7 @@ function shouldEnableClientSideMediaProcessing() {
100
102
  */
101
103
  function mediaUpload(
102
104
  registry,
105
+ settings,
103
106
  {
104
107
  allowedTypes,
105
108
  additionalData = {},
@@ -113,9 +116,13 @@ function mediaUpload(
113
116
  void registry.dispatch( uploadStore ).addItems( {
114
117
  files: Array.from( filesList ),
115
118
  onChange: onFileChange,
116
- onSuccess,
119
+ onSuccess: ( attachments ) => {
120
+ settings?.[ mediaUploadOnSuccessKey ]?.( attachments );
121
+ onSuccess?.( attachments );
122
+ },
117
123
  onBatchSuccess,
118
- onError: ( { message } ) => onError( message ),
124
+ onError: ( error ) =>
125
+ onError( typeof error === 'string' ? error : error?.message ?? '' ),
119
126
  additionalData,
120
127
  allowedTypes,
121
128
  } );
@@ -146,16 +153,38 @@ export const ExperimentalBlockEditorProvider = withRegistryProvider(
146
153
  const isClientSideMediaEnabled =
147
154
  shouldEnableClientSideMediaProcessing();
148
155
 
156
+ // Nested providers (e.g. from useBlockPreview) inherit settings
157
+ // where mediaUpload has already been replaced with the
158
+ // interceptor. Detect this so we skip the replacement and
159
+ // MediaUploadProvider for them — see the longer comment below.
160
+ const isMediaUploadIntercepted =
161
+ !! _settings?.mediaUpload?.__isMediaUploadInterceptor;
162
+
149
163
  const settings = useMemo( () => {
150
- if ( isClientSideMediaEnabled && _settings?.mediaUpload ) {
164
+ if (
165
+ isClientSideMediaEnabled &&
166
+ _settings?.mediaUpload &&
167
+ ! isMediaUploadIntercepted
168
+ ) {
151
169
  // Create a new object so that the original props.settings.mediaUpload is not modified.
170
+ const interceptor = mediaUpload.bind(
171
+ null,
172
+ registry,
173
+ _settings
174
+ );
175
+ interceptor.__isMediaUploadInterceptor = true;
152
176
  return {
153
177
  ..._settings,
154
- mediaUpload: mediaUpload.bind( null, registry ),
178
+ mediaUpload: interceptor,
155
179
  };
156
180
  }
157
181
  return _settings;
158
- }, [ _settings, registry, isClientSideMediaEnabled ] );
182
+ }, [
183
+ _settings,
184
+ registry,
185
+ isClientSideMediaEnabled,
186
+ isMediaUploadIntercepted,
187
+ ] );
159
188
 
160
189
  const { __experimentalUpdateSettings } = unlock(
161
190
  useDispatch( blockEditorStore )
@@ -215,7 +244,21 @@ export const ExperimentalBlockEditorProvider = withRegistryProvider(
215
244
  </SelectionContext.Provider>
216
245
  );
217
246
 
218
- if ( isClientSideMediaEnabled ) {
247
+ // MediaUploadProvider writes the mediaUpload function from
248
+ // _settings into the shared upload-media store so the store can
249
+ // hand files off to the server. useMediaUploadSettings extracts
250
+ // mediaUpload from the original _settings prop — *before* the
251
+ // interceptor replacement above — so the store receives the
252
+ // real server-side upload function.
253
+ //
254
+ // Only the first (outermost) provider should do this.
255
+ // Nested providers (e.g. from useBlockPreview in
256
+ // core/post-template) inherit settings that already contain
257
+ // the interceptor, so their MediaUploadProvider would
258
+ // overwrite the store's server-side function with the
259
+ // interceptor, causing uploads to loop instead of reaching
260
+ // the server.
261
+ if ( isClientSideMediaEnabled && ! isMediaUploadIntercepted ) {
219
262
  return (
220
263
  <MediaUploadProvider
221
264
  settings={ mediaUploadSettings }
@@ -15,6 +15,7 @@ function useMediaUploadSettings( settings = {} ) {
15
15
  () => ( {
16
16
  mediaUpload: settings.mediaUpload,
17
17
  mediaSideload: settings.mediaSideload,
18
+ mediaFinalize: settings.mediaFinalize,
18
19
  maxUploadFileSize: settings.maxUploadFileSize,
19
20
  allowedMimeTypes: settings.allowedMimeTypes,
20
21
  allImageSizes: settings.allImageSizes,
@@ -115,9 +115,16 @@ export function getClosestTabbable(
115
115
  }
116
116
 
117
117
  function isTabCandidate( node ) {
118
- // If it's a block and there are nested focusable nodes, skip because
119
- // there are better candidates.
118
+ // If it's a block wrapper (not itself a contenteditable editing surface)
119
+ // and there are nested focusable nodes, skip because there are better
120
+ // candidates. We must not skip contenteditable nodes that happen to
121
+ // contain links or other focusable inline elements, since those are the
122
+ // correct navigation targets.
123
+ //
124
+ // See https://github.com/WordPress/gutenberg/pull/77474
125
+ // TODO: Consider fixing focus.tabbable
120
126
  if (
127
+ node.contentEditable !== 'true' &&
121
128
  getBlockClientId( node ) &&
122
129
  focus.focusable
123
130
  .find( node )
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { getBlockType } from '@wordpress/blocks';
4
+ import { getBlockType, store as blocksStore } from '@wordpress/blocks';
5
5
  import { PanelBody } from '@wordpress/components';
6
6
  import { useSelect } from '@wordpress/data';
7
7
  import { DataForm } from '@wordpress/dataviews';
8
- import { useMemo } from '@wordpress/element';
8
+ import { useContext, useMemo } from '@wordpress/element';
9
9
  import { __ } from '@wordpress/i18n';
10
10
 
11
11
  /**
@@ -14,6 +14,8 @@ import { __ } from '@wordpress/i18n';
14
14
  import InspectorControls from '../components/inspector-controls';
15
15
  import { useBlockEditingMode } from '../components/block-editing-mode';
16
16
  import { store as blockEditorStore } from '../store';
17
+ import { unlock } from '../lock-unlock';
18
+ import BlockContext from '../components/block-context';
17
19
  import { generateFieldsFromAttributes } from './generate-fields-from-attributes';
18
20
 
19
21
  /**
@@ -45,9 +47,34 @@ function hasAutoGenerateControl( blockTypeAttributes ) {
45
47
  function AutoRegisterControls( { name, clientId, setAttributes } ) {
46
48
  const blockEditingMode = useBlockEditingMode();
47
49
 
50
+ const blockContext = useContext( BlockContext );
51
+
48
52
  const attributes = useSelect(
49
- ( select ) => select( blockEditorStore ).getBlockAttributes( clientId ),
50
- [ clientId ]
53
+ ( select ) => {
54
+ const _attributes =
55
+ select( blockEditorStore ).getBlockAttributes( clientId );
56
+ if ( ! _attributes?.metadata?.bindings ) {
57
+ return _attributes;
58
+ }
59
+
60
+ const { getBlockBindingsSource } = unlock( select( blocksStore ) );
61
+ return Object.entries( _attributes.metadata.bindings ).reduce(
62
+ ( acc, [ attribute, binding ] ) => {
63
+ const source = getBlockBindingsSource( binding.source );
64
+ if ( ! source ) {
65
+ return acc;
66
+ }
67
+ const values = source.getValues( {
68
+ select,
69
+ context: blockContext,
70
+ bindings: { [ attribute ]: binding },
71
+ } );
72
+ return { ...acc, ...values };
73
+ },
74
+ _attributes
75
+ );
76
+ },
77
+ [ blockContext, clientId ]
51
78
  );
52
79
 
53
80
  const blockType = getBlockType( name );
@@ -1,34 +1,12 @@
1
1
  /**
2
- * WordPress dependencies
3
- */
4
- import { addFilter } from '@wordpress/hooks';
5
- import { createHigherOrderComponent } from '@wordpress/compose';
6
-
7
- /**
8
- * Adds crossorigin and credentialless attributes to elements as needed.
2
+ * Adds crossorigin="anonymous" to an element if missing.
9
3
  *
10
4
  * @param {Element} el The element to modify.
11
5
  */
12
- function addCrossOriginAttributes( el ) {
13
- // Add the crossorigin attribute if missing.
6
+ function addCrossOriginAttribute( el ) {
14
7
  if ( ! el.hasAttribute( 'crossorigin' ) ) {
15
8
  el.setAttribute( 'crossorigin', 'anonymous' );
16
9
  }
17
-
18
- // For iframes, add the credentialless attribute.
19
- if ( el.nodeName === 'IFRAME' && ! el.hasAttribute( 'credentialless' ) ) {
20
- // Do not modify the iframed editor canvas.
21
- if ( el.getAttribute( 'src' )?.startsWith( 'blob:' ) ) {
22
- return;
23
- }
24
-
25
- el.setAttribute( 'credentialless', '' );
26
-
27
- // Reload the iframe to ensure the new attribute is taken into account.
28
- const origSrc = el.getAttribute( 'src' ) || '';
29
- el.setAttribute( 'src', '' );
30
- el.setAttribute( 'src', origSrc );
31
- }
32
10
  }
33
11
 
34
12
  // Only add the mutation observer if the site is cross-origin isolated.
@@ -49,59 +27,18 @@ if ( window.crossOriginIsolated ) {
49
27
  return;
50
28
  }
51
29
 
52
- el.querySelectorAll(
53
- 'img,source,script,video,link,iframe'
54
- ).forEach( ( v ) => {
55
- addCrossOriginAttributes( v );
56
- } );
57
-
58
- if ( el.nodeName === 'IFRAME' ) {
59
- const iframeNode = el;
60
-
61
- /*
62
- * Sandboxed iframes should not get modified. For example embedding a tweet served in a sandboxed
63
- * iframe, the tweet itself would not be modified.
64
- */
65
- const isEmbedSandboxIframe =
66
- iframeNode.classList.contains(
67
- 'components-sandbox'
68
- );
69
-
70
- if ( ! isEmbedSandboxIframe ) {
71
- iframeNode.addEventListener( 'load', () => {
72
- try {
73
- if (
74
- iframeNode.contentDocument &&
75
- iframeNode.contentDocument.body
76
- ) {
77
- observer.observe(
78
- iframeNode.contentDocument,
79
- {
80
- childList: true,
81
- attributes: true,
82
- subtree: true,
83
- }
84
- );
85
- }
86
- } catch ( e ) {
87
- // Iframe may be cross-origin or otherwise inaccessible.
88
- // Silently ignore these cases.
89
- }
90
- } );
30
+ el.querySelectorAll( 'source,script,video,link' ).forEach(
31
+ ( v ) => {
32
+ addCrossOriginAttribute( v );
91
33
  }
92
- }
34
+ );
93
35
 
94
36
  if (
95
- [
96
- 'IMG',
97
- 'SOURCE',
98
- 'SCRIPT',
99
- 'VIDEO',
100
- 'LINK',
101
- 'IFRAME',
102
- ].includes( el.nodeName )
37
+ [ 'SOURCE', 'SCRIPT', 'VIDEO', 'LINK' ].includes(
38
+ el.nodeName
39
+ )
103
40
  ) {
104
- addCrossOriginAttributes( el );
41
+ addCrossOriginAttribute( el );
105
42
  }
106
43
  } );
107
44
  } );
@@ -134,39 +71,3 @@ if ( window.crossOriginIsolated ) {
134
71
 
135
72
  startObservingBody();
136
73
  }
137
-
138
- // Only apply the embed preview filter when cross-origin isolated.
139
- if ( window.crossOriginIsolated ) {
140
- const supportsCredentialless =
141
- 'credentialless' in window.HTMLIFrameElement.prototype;
142
-
143
- const disableEmbedPreviews = createHigherOrderComponent(
144
- ( BlockEdit ) =>
145
- function DisableEmbedPreviews( props ) {
146
- if ( 'core/embed' !== props.name ) {
147
- return <BlockEdit { ...props } />;
148
- }
149
-
150
- // List of embeds that do not support a preview is from packages/block-library/src/embed/variations.js.
151
- const previewable =
152
- supportsCredentialless &&
153
- ! [ 'facebook', 'smugmug' ].includes(
154
- props.attributes.providerNameSlug
155
- );
156
-
157
- return (
158
- <BlockEdit
159
- { ...props }
160
- attributes={ { ...props.attributes, previewable } }
161
- />
162
- );
163
- },
164
- 'withDisabledEmbedPreview'
165
- );
166
-
167
- addFilter(
168
- 'editor.BlockEdit',
169
- 'media-experiments/disable-embed-previews',
170
- disableEmbedPreviews
171
- );
172
- }
@@ -1,12 +1,14 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useMemo } from '@wordpress/element';
5
- import { useSelect } from '@wordpress/data';
4
+ import { useEffect, useMemo } from '@wordpress/element';
5
+ import { useDispatch, useSelect } from '@wordpress/data';
6
6
  import { useInstanceId } from '@wordpress/compose';
7
7
  import { getBlockType, hasBlockSupport } from '@wordpress/blocks';
8
8
  import { __, sprintf } from '@wordpress/i18n';
9
9
  import { processCSSNesting } from '@wordpress/global-styles-engine';
10
+ import { useBlockEditingMode } from '../components/block-editing-mode';
11
+ import { store as noticesStore } from '@wordpress/notices';
10
12
 
11
13
  /**
12
14
  * Internal dependencies
@@ -33,6 +35,11 @@ const EMPTY_STYLE = {};
33
35
  * @param {Object} props.style Block style attribute.
34
36
  */
35
37
  function CustomCSSControl( { blockName, setAttributes, style } ) {
38
+ const blockEditingMode = useBlockEditingMode();
39
+
40
+ if ( blockEditingMode !== 'default' ) {
41
+ return null;
42
+ }
36
43
  const blockType = getBlockType( blockName );
37
44
 
38
45
  function onChange( newStyle ) {
@@ -63,6 +70,8 @@ function CustomCSSControl( { blockName, setAttributes, style } ) {
63
70
  );
64
71
  }
65
72
 
73
+ const CUSTOM_CSS_WARNING_NOTICE_ID = 'custom-css-edit-warning';
74
+
66
75
  function CustomCSSEdit( { clientId, name, setAttributes } ) {
67
76
  const { style, canEditCSS } = useSelect(
68
77
  ( select ) => {
@@ -107,6 +116,31 @@ function useBlockProps( { style } ) {
107
116
  customCSS.trim().length > 0 &&
108
117
  validateCSS( customCSS );
109
118
 
119
+ const canEditCSS = useSelect(
120
+ ( select ) => select( blockEditorStore ).getSettings().canEditCSS,
121
+ []
122
+ );
123
+
124
+ const { createWarningNotice } = useDispatch( noticesStore );
125
+
126
+ // Show a warning notice when the user lacks edit_css and a block has
127
+ // custom CSS. The fixed notice ID ensures only one notice is shown
128
+ // regardless of how many blocks have CSS.
129
+ const hasCustomCSS = !! customCSS?.trim();
130
+ useEffect( () => {
131
+ if ( ! canEditCSS && hasCustomCSS ) {
132
+ createWarningNotice(
133
+ __(
134
+ 'This post contains blocks with custom CSS. You do not have permission to edit CSS. If you save this post, the custom CSS will be removed.'
135
+ ),
136
+ {
137
+ id: CUSTOM_CSS_WARNING_NOTICE_ID,
138
+ isDismissible: true,
139
+ }
140
+ );
141
+ }
142
+ }, [ canEditCSS, hasCustomCSS, createWarningNotice ] );
143
+
110
144
  const customCSSIdentifier = useInstanceId(
111
145
  CUSTOM_CSS_INSTANCE_REFERENCE,
112
146
  'wp-custom-css'