diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md
index cfb95389ba039..7a2051aa91aee 100644
--- a/docs/reference-guides/data/data-core-block-editor.md
+++ b/docs/reference-guides/data/data-core-block-editor.md
@@ -1087,6 +1087,10 @@ _Returns_
- `boolean`: Whether block is first in multi-selection.
+### isIframeIncompatible
+
+Undocumented declaration.
+
### isLastBlockChangePersistent
Returns true if the most recent block change is be considered persistent, or
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 129a4f6f178bb..64f224095c0da 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -56,6 +56,7 @@
"@wordpress/preferences": "file:../preferences",
"@wordpress/private-apis": "file:../private-apis",
"@wordpress/rich-text": "file:../rich-text",
+ "@wordpress/server-side-render": "file:../server-side-render",
"@wordpress/shortcode": "file:../shortcode",
"@wordpress/style-engine": "file:../style-engine",
"@wordpress/token-list": "file:../token-list",
diff --git a/packages/block-editor/src/components/block-edit/edit.js b/packages/block-editor/src/components/block-edit/edit.js
index 1154b99efebab..f07f50dab8412 100644
--- a/packages/block-editor/src/components/block-edit/edit.js
+++ b/packages/block-editor/src/components/block-edit/edit.js
@@ -12,12 +12,23 @@ import {
hasBlockSupport,
getBlockType,
} from '@wordpress/blocks';
-import { useContext, useMemo } from '@wordpress/element';
+import { useContext, useMemo, useRef } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import ServerSideRender from '@wordpress/server-side-render';
+import { useResizeObserver } from '@wordpress/compose';
/**
* Internal dependencies
*/
import BlockContext from '../block-context';
+import { useSelect } from '@wordpress/data';
+import { store } from '../../store';
+import {
+ useBlockProps,
+ DisableBlockProps,
+} from '../block-list/use-block-props';
+import Warning from '../warning';
+import BlockPopover from '../block-popover';
/**
* Default value used for blocks which do not define their own context needs,
@@ -29,8 +40,65 @@ import BlockContext from '../block-context';
*/
const DEFAULT_BLOCK_CONTEXT = {};
+function LoadingResponsePlaceholder() {
+ return { __( 'Loading preview…' ) };
+}
+
+function IframeCompat( {
+ clientId,
+ blockType,
+ attributes,
+ isSelected,
+ children,
+} ) {
+ const isIframeIncompatible = useSelect(
+ ( select ) => select( store ).isIframeIncompatible( clientId ),
+ [ clientId ]
+ );
+ const [ resizeListener, sizes ] = useResizeObserver();
+ const ref = useRef();
+ const blockProps = useBlockProps( {
+ ref,
+ style: {
+ height: isSelected ? sizes?.height : undefined,
+ },
+ } );
+
+ if ( ! isIframeIncompatible ) {
+ return children;
+ }
+
+ return (
+
+ { isSelected && (
+
+
+
+ { resizeListener }
+ { children }
+
+
+
+ ) }
+ { ! isSelected && (
+
+ ) }
+
+ );
+}
+
export const Edit = ( props ) => {
- const { attributes = {}, name } = props;
+ const { attributes = {}, name, clientId, isSelected } = props;
const blockType = getBlockType( name );
const blockContext = useContext( BlockContext );
@@ -55,7 +123,16 @@ export const Edit = ( props ) => {
const Component = blockType.edit || blockType.save;
if ( blockType.apiVersion > 1 ) {
- return ;
+ return (
+
+
+
+ );
}
// Generate a class name for the block's editable form.
@@ -69,7 +146,18 @@ export const Edit = ( props ) => {
);
return (
-
+
+
+
);
};
diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js
index ca6bb4355f52d..b044a82e3493f 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/index.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/index.js
@@ -6,7 +6,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
-import { useContext } from '@wordpress/element';
+import { useContext, createContext } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import {
__unstableGetBlockProps as getBlockProps,
@@ -34,6 +34,7 @@ import { useEventHandlers } from './use-selected-block-event-handlers';
import { useNavModeExit } from './use-nav-mode-exit';
import { useBlockRefProvider } from './use-block-refs';
import { useIntersectionObserver } from './use-intersection-observer';
+import { useNonReactObserver } from './use-non-react-observer';
import { store as blockEditorStore } from '../../../store';
import useBlockOverlayActive from '../../block-content-overlay';
@@ -43,6 +44,8 @@ import useBlockOverlayActive from '../../block-content-overlay';
*/
const BLOCK_ANIMATION_THRESHOLD = 200;
+export const DisableBlockProps = createContext();
+
/**
* This hook is used to lightly mark an element as a block element. The element
* should be the outermost element of a block. Call this hook and pass the
@@ -66,6 +69,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
wrapperProps = {},
isAligned,
} = useContext( BlockListBlockContext );
+ const disableBlockProps = useContext( DisableBlockProps );
const {
index,
mode,
@@ -117,9 +121,6 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
);
const hasOverlay = useBlockOverlayActive( clientId );
-
- // translators: %s: Type of block (i.e. Text, Image etc)
- const blockLabel = sprintf( __( 'Block: %s' ), blockTitle );
const htmlSuffix = mode === 'html' && ! __unstableIsHtml ? '-visual' : '';
const mergedRefs = useMergeRefs( [
props.ref,
@@ -137,6 +138,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
triggerAnimationOnChange: index,
} ),
useDisabled( { isDisabled: ! hasOverlay } ),
+ useNonReactObserver( clientId ),
] );
const blockEditContext = useBlockEditContext();
@@ -147,6 +149,20 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
);
}
+ const additionalClassNames = [
+ useBlockClassNames( clientId ),
+ useBlockDefaultClassName( clientId ),
+ useBlockCustomClassName( clientId ),
+ useBlockMovingModeClassNames( clientId ),
+ ];
+
+ if ( disableBlockProps ) {
+ return {};
+ }
+
+ // translators: %s: Type of block (i.e. Text, Image etc)
+ const blockLabel = sprintf( __( 'Block: %s' ), blockTitle );
+
return {
tabIndex: 0,
...wrapperProps,
@@ -167,10 +183,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
className,
props.className,
wrapperProps.className,
- useBlockClassNames( clientId ),
- useBlockDefaultClassName( clientId ),
- useBlockCustomClassName( clientId ),
- useBlockMovingModeClassNames( clientId )
+ ...additionalClassNames
),
style: { ...wrapperProps.style, ...props.style },
};
diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-non-react-observer.js b/packages/block-editor/src/components/block-list/use-block-props/use-non-react-observer.js
new file mode 100644
index 0000000000000..09142059752d0
--- /dev/null
+++ b/packages/block-editor/src/components/block-list/use-block-props/use-non-react-observer.js
@@ -0,0 +1,46 @@
+/**
+ * WordPress dependencies
+ */
+import { useRefEffect } from '@wordpress/compose';
+import { useDispatch } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import { store as blockEditorStore } from '../../../store';
+
+export function useNonReactObserver( clientId ) {
+ const { __unstableIframeIncompatible } = useDispatch( blockEditorStore );
+ return useRefEffect( ( node ) => {
+ const observer = new node.ownerDocument.defaultView.MutationObserver(
+ ( mutationList ) => {
+ for ( const mutation of mutationList ) {
+ for ( const addedNode of mutation.addedNodes ) {
+ if ( ! addedNode.isConnected ) continue;
+
+ if (
+ addedNode.isContentEditable ||
+ addedNode.parentElement.isContentEditable
+ )
+ continue;
+
+ if (
+ Object.keys( addedNode ).some( ( i ) => {
+ // Yes, React could change this at any point,
+ // but we'll know when we update the version.
+ return i.startsWith( '__react' );
+ } )
+ )
+ continue;
+
+ __unstableIframeIncompatible( clientId );
+ }
+ }
+ }
+ );
+ observer.observe( node, { childList: true, subtree: true } );
+ return () => {
+ observer.disconnect( node );
+ };
+ }, [] );
+}
diff --git a/packages/block-editor/src/components/block-popover/use-popover-scroll.js b/packages/block-editor/src/components/block-popover/use-popover-scroll.js
index 8aeb768e302f6..e7c002514f0ab 100644
--- a/packages/block-editor/src/components/block-popover/use-popover-scroll.js
+++ b/packages/block-editor/src/components/block-popover/use-popover-scroll.js
@@ -2,6 +2,7 @@
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose';
+import { getScrollContainer } from '@wordpress/dom';
/**
* Allow scrolling "through" popovers over the canvas. This is only called for
@@ -19,14 +20,28 @@ function usePopoverScroll( scrollableRef ) {
function onWheel( event ) {
const { deltaX, deltaY } = event;
+ const scrollContainer = getScrollContainer(
+ scrollableRef.current
+ );
+ const { ownerDocument } = scrollContainer;
+ const { defaultView } = ownerDocument;
+ const windowScroll =
+ scrollContainer === ownerDocument.body ||
+ scrollContainer === ownerDocument.documentElement;
scrollableRef.current.scrollBy( deltaX, deltaY );
+
+ if ( windowScroll ) {
+ defaultView.scrollBy( 0, deltaY );
+ } else {
+ scrollContainer.scrollLeft += deltaX;
+ scrollContainer.scrollTop += deltaY;
+ }
+
+ event.preventDefault();
}
- // Tell the browser that we do not call event.preventDefault
- // See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
- const options = { passive: true };
- node.addEventListener( 'wheel', onWheel, options );
+ node.addEventListener( 'wheel', onWheel );
return () => {
- node.removeEventListener( 'wheel', onWheel, options );
+ node.removeEventListener( 'wheel', onWheel );
};
},
[ scrollableRef ]
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index 98bdf1bffc78c..9a749a6da0a47 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -1264,6 +1264,13 @@ export function toggleBlockMode( clientId ) {
};
}
+export function __unstableIframeIncompatible( clientId ) {
+ return {
+ type: 'IFRAME_INCOMPATIBLE',
+ clientId,
+ };
+}
+
/**
* Returns an action object used in signalling that the user has begun to type.
*
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index c207df38692b2..6b04c76e19f9a 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -1507,6 +1507,18 @@ export function blocksMode( state = {}, action ) {
return state;
}
+export function iframeIncompatible( state = {}, action ) {
+ if ( action.type === 'IFRAME_INCOMPATIBLE' ) {
+ const { clientId } = action;
+ return {
+ ...state,
+ [ clientId ]: true,
+ };
+ }
+
+ return state;
+}
+
/**
* Reducer returning the block insertion point visibility, either null if there
* is not an explicit insertion point assigned, or an object of its `index` and
@@ -1873,6 +1885,7 @@ export default combineReducers( {
isSelectionEnabled,
initialPosition,
blocksMode,
+ iframeIncompatible,
blockListSettings,
insertionPoint,
template,
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index 543da7dd1debb..2452a744ec555 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -1279,6 +1279,10 @@ export function getBlockMode( state, clientId ) {
return state.blocksMode[ clientId ] || 'visual';
}
+export function isIframeIncompatible( state, clientId ) {
+ return state.iframeIncompatible[ clientId ] || false;
+}
+
/**
* Returns true if the user is typing, or false otherwise.
*