-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Enable access to block settings within UBE #48435
Changes from all commits
5bd1081
fcfb7d4
a5b08b1
25dddd0
ea8d18e
7c1773b
b7f10f5
a7eb9a2
752a659
7233623
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,19 +25,12 @@ window.getHTMLPostContent = () => { | |
}; | ||
|
||
window.insertBlock = ( blockHTML ) => { | ||
const { blockEditorSelect, blockEditorDispatch } = | ||
window.getBlockEditorStore(); | ||
|
||
// Setup the editor with the inserted block. | ||
const post = window.wp.data.select( 'core/editor' ).getCurrentPost(); | ||
window.wp.data | ||
.dispatch( 'core/editor' ) | ||
.setupEditor( post, { content: blockHTML } ); | ||
|
||
// Select the first block. | ||
const clientId = blockEditorSelect.getBlocks()[ 0 ].clientId; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This often threw an error where the first block was |
||
blockEditorDispatch.selectBlock( clientId ); | ||
|
||
window.contentIncerted = true; | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,167 @@ | ||
// Listeners for native context menu visibility changes. | ||
let isContextMenuVisible = false; | ||
const hideContextMenuListeners = []; | ||
|
||
window.onShowContextMenu = () => { | ||
isContextMenuVisible = true; | ||
}; | ||
window.onHideContextMenu = () => { | ||
isContextMenuVisible = false; | ||
while ( hideContextMenuListeners.length > 0 ) { | ||
const listener = hideContextMenuListeners.pop(); | ||
listener(); | ||
} | ||
}; | ||
|
||
/* | ||
This is a fix for a text selection quirk in the UBE. | ||
It notifies the Android app to dismiss the text selection | ||
context menu when certain menu items are tapped. This is | ||
done via the 'hideTextSelectionContextMenu' method, which | ||
is sent back to the Android app, where the dismissal is | ||
then handle. See PR for further details: | ||
https://github.com/WordPress/gutenberg/pull/34668 | ||
*/ | ||
window.addEventListener( | ||
'click', | ||
( event ) => { | ||
const selected = document.getSelection(); | ||
if ( ! isContextMenuVisible || ! selected || ! selected.toString() ) { | ||
return; | ||
/** | ||
* Detects whether the user agent is Android. | ||
* | ||
* @return {boolean} Whether the user agent is Android. | ||
*/ | ||
function isAndroid() { | ||
return !! window.navigator.userAgent.match( /Android/ ); | ||
} | ||
|
||
/** | ||
* This is a fix for a text selection quirk in the UBE. It notifies the Android | ||
* app to dismiss the text selection context menu when certain menu items are | ||
* tapped. This is done via the 'hideTextSelectionContextMenu' method, which | ||
* is sent back to the Android app, where the dismissal is then handle. | ||
* | ||
* @return {void} | ||
* @see https://github.com/WordPress/gutenberg/pull/34668 | ||
*/ | ||
function manageTextSelectonContextMenu() { | ||
// Listeners for native context menu visibility changes. | ||
let isContextMenuVisible = false; | ||
const hideContextMenuListeners = []; | ||
|
||
window.onShowContextMenu = () => { | ||
isContextMenuVisible = true; | ||
}; | ||
window.onHideContextMenu = () => { | ||
isContextMenuVisible = false; | ||
while ( hideContextMenuListeners.length > 0 ) { | ||
const listener = hideContextMenuListeners.pop(); | ||
listener(); | ||
} | ||
}; | ||
|
||
// Check if the event is triggered by a dropdown | ||
// toggle button. | ||
const dropdownToggles = document.querySelectorAll( | ||
'.components-dropdown-menu > button' | ||
); | ||
let currentToggle; | ||
for ( const node of dropdownToggles.values() ) { | ||
if ( node.contains( event.target ) ) { | ||
currentToggle = node; | ||
break; | ||
window.addEventListener( | ||
'click', | ||
( event ) => { | ||
const selected = document.getSelection(); | ||
if ( | ||
! isContextMenuVisible || | ||
! selected || | ||
! selected.toString() | ||
) { | ||
return; | ||
} | ||
} | ||
|
||
// Hide text selection context menu when the click | ||
// is triggered by a dropdown toggle. | ||
// | ||
// NOTE: The event propagation is prevented because | ||
// it will be dispatched after the context menu | ||
// is hidden. | ||
if ( currentToggle ) { | ||
event.stopPropagation(); | ||
hideContextMenuListeners.push( () => currentToggle.click() ); | ||
window.wpwebkit.hideTextSelectionContextMenu(); | ||
// Check if the event is triggered by a dropdown | ||
// toggle button. | ||
const dropdownToggles = document.querySelectorAll( | ||
'.components-dropdown-menu > button' | ||
); | ||
let currentToggle; | ||
for ( const node of dropdownToggles.values() ) { | ||
if ( node.contains( event.target ) ) { | ||
currentToggle = node; | ||
break; | ||
} | ||
} | ||
|
||
// Hide text selection context menu when the click | ||
// is triggered by a dropdown toggle. | ||
// | ||
// NOTE: The event propagation is prevented because | ||
// it will be dispatched after the context menu | ||
// is hidden. | ||
if ( currentToggle ) { | ||
event.stopPropagation(); | ||
hideContextMenuListeners.push( () => currentToggle.click() ); | ||
window.wpwebkit.hideTextSelectionContextMenu(); | ||
} | ||
}, | ||
true | ||
); | ||
} | ||
|
||
if ( isAndroid() ) { | ||
manageTextSelectonContextMenu(); | ||
} | ||
|
||
const editor = document.querySelector( '#editor' ); | ||
|
||
function _toggleBlockSelectedClass( isBlockSelected ) { | ||
if ( isBlockSelected ) { | ||
editor.classList.add( 'is-block-selected' ); | ||
} else { | ||
editor.classList.remove( 'is-block-selected' ); | ||
} | ||
} | ||
|
||
/** @typedef {import('@wordpress/data').WPDataRegistry} WPDataRegistry */ | ||
|
||
/** | ||
* Toggle the `is-block-selected` class on the editor container when a block is | ||
* selected. This is used to hide the sidebar toggle button when a block is not | ||
* selected. | ||
* | ||
* @param {WPDataRegistry} registry Data registry. | ||
* @return {WPDataRegistry} Modified data registry. | ||
*/ | ||
function toggleBlockSelectedStyles( registry ) { | ||
return { | ||
dispatch: ( namespace ) => { | ||
const namespaceName = | ||
typeof namespace === 'string' ? namespace : namespace.name; | ||
const actions = { ...registry.dispatch( namespaceName ) }; | ||
|
||
const originalSelectBlockAction = actions.selectBlock; | ||
actions.selectBlock = ( ...args ) => { | ||
_toggleBlockSelectedClass( true ); | ||
return originalSelectBlockAction( ...args ); | ||
}; | ||
|
||
const originalClearSelectedBlockAction = actions.clearSelectedBlock; | ||
actions.clearSelectedBlock = ( ...args ) => { | ||
_toggleBlockSelectedClass( false ); | ||
return originalClearSelectedBlockAction( ...args ); | ||
}; | ||
|
||
return actions; | ||
}, | ||
}; | ||
} | ||
|
||
window.wp.data.use( toggleBlockSelectedStyles ); | ||
|
||
// The editor-canvas iframe relies upon `srcdoc`, which does not trigger a | ||
// `load` event. Thus, we must poll for the iframe to be ready. | ||
let overrideAttempts = 0; | ||
const overrideInterval = setInterval( () => { | ||
Comment on lines
+126
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I explored a few different approaches for awaiting the
|
||
overrideAttempts++; | ||
const overrideStyles = document.querySelector( '#editor-style-overrides' ); | ||
const canvasIframe = document.querySelector( | ||
'iframe[name="editor-canvas"]' | ||
); | ||
|
||
if ( | ||
overrideStyles && | ||
canvasIframe && | ||
canvasIframe.contentDocument && | ||
canvasIframe.contentDocument.documentElement | ||
) { | ||
clearInterval( overrideInterval ); | ||
|
||
// Clone the editor styles so that they can be copied to the iframe, as | ||
// elements within an iframe cannot be styled from the parent context. | ||
const overrideStylesClone = overrideStyles.cloneNode( true ); | ||
overrideStylesClone.id = 'editor-styles-overrides-2'; | ||
// Append to document rather than the head, as React will remove this | ||
// mutation. | ||
canvasIframe.contentDocument.documentElement.appendChild( | ||
Comment on lines
+148
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
overrideStylesClone | ||
); | ||
|
||
// Select the first block. | ||
const { blockEditorSelect, blockEditorDispatch } = | ||
window.getBlockEditorStore(); | ||
const firstBlock = blockEditorSelect.getBlocks()[ 0 ]; | ||
if ( firstBlock ) { | ||
blockEditorDispatch.selectBlock( firstBlock.clientId ); | ||
} | ||
}, | ||
true | ||
); | ||
} | ||
|
||
// Safeguard against an infinite loop. | ||
if ( overrideAttempts > 100 ) { | ||
clearInterval( overrideInterval ); | ||
} | ||
}, 300 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rudimentary stripping of whitespace inadvertently modifies the CSS selectors, causing them to be interpreted as completely different selectors. E.g.
.ancestor .descendent
is different from.sibling-1.sibling-2
. Ideally, a proper CSS minification is utilized long term.