-
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
Make the shortcuts provider optional #54080
Changes from all commits
a7151ed
88b69b2
ab7c07c
2a6247c
a94eb2d
0138f13
93537de
82a856b
687c653
96d4684
e10d9d2
42015f0
dde6447
7ff56bf
f419388
39b2f74
95e3607
cdc70bd
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 |
---|---|---|
|
@@ -31,6 +31,30 @@ import { useWritingFlow } from '../writing-flow'; | |
import { useCompatibilityStyles } from './use-compatibility-styles'; | ||
import { store as blockEditorStore } from '../../store'; | ||
|
||
function bubbleEvent( event, Constructor, frame ) { | ||
const init = {}; | ||
|
||
for ( const key in event ) { | ||
init[ key ] = event[ key ]; | ||
} | ||
|
||
if ( event instanceof frame.ownerDocument.defaultView.MouseEvent ) { | ||
const rect = frame.getBoundingClientRect(); | ||
init.clientX += rect.left; | ||
init.clientY += rect.top; | ||
} | ||
|
||
const newEvent = new Constructor( event.type, init ); | ||
if ( init.defaultPrevented ) { | ||
newEvent.preventDefault(); | ||
} | ||
const cancelled = ! frame.dispatchEvent( newEvent ); | ||
|
||
if ( cancelled ) { | ||
event.preventDefault(); | ||
} | ||
} | ||
|
||
/** | ||
* Bubbles some event types (keydown, keypress, and dragover) to parent document | ||
* document to ensure that the keyboard shortcuts and drag and drop work. | ||
|
@@ -39,42 +63,30 @@ import { store as blockEditorStore } from '../../store'; | |
* should be context dependent, e.g. actions on blocks like Cmd+A should not | ||
* work globally outside the block editor. | ||
* | ||
* @param {Document} doc Document to attach listeners to. | ||
* @param {Document} iframeDocument Document to attach listeners to. | ||
*/ | ||
function bubbleEvents( doc ) { | ||
const { defaultView } = doc; | ||
const { frameElement } = defaultView; | ||
|
||
function bubbleEvent( event ) { | ||
const prototype = Object.getPrototypeOf( event ); | ||
const constructorName = prototype.constructor.name; | ||
const Constructor = window[ constructorName ]; | ||
|
||
const init = {}; | ||
|
||
for ( const key in event ) { | ||
init[ key ] = event[ key ]; | ||
function useBubbleEvents( iframeDocument ) { | ||
return useRefEffect( ( body ) => { | ||
const { defaultView } = iframeDocument; | ||
const { frameElement } = defaultView; | ||
const eventTypes = [ 'dragover', 'mousemove' ]; | ||
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. So we're not bubbling keydown in the end? 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. Is this refactoring helping with anything in particular or is it a remnant from an earlier iteration? 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. No, the refactoring is not doing anything I can restore the previous "bubbleEvents" here but we need the inner "bubbleEvent" function to be shared so we can use it when bubbling the keydown events as a React event handler. |
||
const handlers = {}; | ||
for ( const name of eventTypes ) { | ||
handlers[ name ] = ( event ) => { | ||
const prototype = Object.getPrototypeOf( event ); | ||
const constructorName = prototype.constructor.name; | ||
const Constructor = window[ constructorName ]; | ||
bubbleEvent( event, Constructor, frameElement ); | ||
}; | ||
body.addEventListener( name, handlers[ name ] ); | ||
} | ||
|
||
if ( event instanceof defaultView.MouseEvent ) { | ||
const rect = frameElement.getBoundingClientRect(); | ||
init.clientX += rect.left; | ||
init.clientY += rect.top; | ||
} | ||
|
||
const newEvent = new Constructor( event.type, init ); | ||
const cancelled = ! frameElement.dispatchEvent( newEvent ); | ||
|
||
if ( cancelled ) { | ||
event.preventDefault(); | ||
} | ||
} | ||
|
||
const eventTypes = [ 'dragover', 'mousemove' ]; | ||
|
||
for ( const name of eventTypes ) { | ||
doc.addEventListener( name, bubbleEvent ); | ||
} | ||
return () => { | ||
for ( const name of eventTypes ) { | ||
body.removeEventListener( name, handlers[ name ] ); | ||
} | ||
}; | ||
} ); | ||
} | ||
|
||
function Iframe( { | ||
|
@@ -117,7 +129,6 @@ function Iframe( { | |
const { documentElement } = contentDocument; | ||
iFrameDocument = contentDocument; | ||
|
||
bubbleEvents( contentDocument ); | ||
clearerRef( documentElement ); | ||
|
||
// Ideally ALL classes that are added through get_body_class should | ||
|
@@ -182,6 +193,7 @@ function Iframe( { | |
|
||
const disabledRef = useDisabled( { isDisabled: ! readonly } ); | ||
const bodyRef = useMergeRefs( [ | ||
useBubbleEvents( iframeDocument ), | ||
contentRef, | ||
clearerRef, | ||
writingFlowRef, | ||
|
@@ -251,13 +263,28 @@ function Iframe( { | |
> | ||
{ iframeDocument && | ||
createPortal( | ||
// We want to prevent React events from bubbling throught the iframe | ||
// we bubble these manually. | ||
/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ | ||
<body | ||
ref={ bodyRef } | ||
className={ classnames( | ||
'block-editor-iframe__body', | ||
'editor-styles-wrapper', | ||
...bodyClasses | ||
) } | ||
onKeyDown={ ( event ) => { | ||
// This stopPropagation call ensures React doesn't create a syncthetic event to bubble this event | ||
// which would result in two React events being bubbled throught the iframe. | ||
event.stopPropagation(); | ||
const { defaultView } = iframeDocument; | ||
const { frameElement } = defaultView; | ||
bubbleEvent( | ||
event, | ||
window.KeyboardEvent, | ||
frameElement | ||
); | ||
} } | ||
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. Why are we listening for the React event instead of native event? 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. That's the only way to basically prevent the "double event bubbling" properly. If I use 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. There's something weird about this, but I guess we can investigate later. |
||
> | ||
{ contentResizeListener } | ||
<StyleProvider document={ iframeDocument }> | ||
|
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.
I'm just looking into the issue over in #55074:
Why is this guarded against a check for
event instanceof frame.ownerDocument.defaultView.MouseEvent
? I notice if I change this toif ( frame ) {
I can get the drag position of the block drag chip (i.e. dragging from the inserter over into the editor canvas) working properly again. So it seems this is returningfalse
for some situations where we still want the offset to be applied, so that drag positioning will be correct. In this example, I think it might mean drag events that originated from outside the iframe?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.
I don't really know, this code was there long before my PR. I'm guessing we should do this for all events that have "clientX" and "clientY" (we may want to exclude keyboard events for instance...)
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.
Thanks for confirming, I'll have a play around 🙂