-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Allow multi-select on iOS Safari/touch devices #63671
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @iamthomasbishop, @porg. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Size Change: +2.41 kB (+0.14%) Total Size: 1.77 MB
ℹ️ View Unchanged
|
8f32a38
to
51a5ce9
Compare
if ( ownerDocument.activeElement !== element ) { | ||
// If it is not, we can stop listening for selection changes. We | ||
// resume listening when the element is focused. | ||
ownerDocument.removeEventListener( |
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.
To do: it would be good to attach one event handler in the first instance, then reuse it for all instances.
4b74ad5
to
31cfb86
Compare
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.
It's a very specialized PR with selections and events, so I took a first look and left some questions to start understanding better. Sorry if some of them might seem obvious to you.
Does your redirection approach mean that we continuously move focus on every related event or it's just about keeping the new focused node (writing flow wrapper) and redirecting the event to rich text so it behaves as if it had focus?
|
||
const init = {}; | ||
|
||
for ( const key in event ) { |
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.
Why not just spread the object here?
packages/block-editor/src/components/writing-flow/use-arrow-nav.js
Outdated
Show resolved
Hide resolved
export default function useEventRedirect() { | ||
return useRefEffect( ( node ) => { | ||
function onInput( event ) { | ||
if ( event.target !== node || event.__isRedirected ) { |
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.
When this event.__isRedirected
is true
?
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.
See 20 lines below :)
return; | ||
} | ||
} | ||
|
||
if ( event.keyCode === ENTER ) { | ||
if ( event.shiftKey || __unstableIsFullySelected() ) { |
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.
Not from here, but curious how Enter
and shift
are used.. Any chance you remember?
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.
Yes, this is Enter behaviour for partial selection. So select two paragraphs partially and press Enter, this handles split for them.
] ); | ||
|
||
// Ensure that the newly created empty block is focused. | ||
await expect.poll( editor.getBlocks ).toHaveLength( 3 ); | ||
await expect.poll( editor.getBlocks ).toHaveLength( 2 ); |
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.
Why you had to change this?
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.
Because it's the only thing in the e2e tests that this PR changed in behaviour. Arrow up selects two blocks. Then press Backspace. Instead of leaving an empty block, it no longer leaves an empty block and puts selection at the end of the previous block.
Yes, we keep focus on the wrapper, and instead clone the event and dispatch it onto the content editable element that has the selection. I mention in the PR description that I also tried maintaining focus on the original content editable, but this is pretty fragile as the browser keeps setting it back to writing flow. |
1b2ea33
to
1cfdedb
Compare
8403c2b
to
55d09d3
Compare
Flaky tests detected in 6674904. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/10392006794
|
6674904
to
8579290
Compare
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.
LGTM Ella, thank you!
🎉 This stone incidentally got another bird #64617. |
What?
Fixes #16206.
Why?
Currently it's not possible to select multiple block on iOS at all.
How?
As soon as there's an uncollapsed selection, prepare for multi-selection with the content editable wrapper.
Note that we already do this for pointer device selection, when the pointer drags out of a block, and also whenever you arrow select out of a block.
The one consequence of doing this is that focus will move from the rich text element in the block to the writing flow wrapper. There's a potential fix by always forcing focus back to the original rich text element, but the browser will continuously put focus back to writing flow whenever an event happens. The other solution, which I have implemented in this PR, is to dispatch a cloned event on the rich text element that does not bubble, so all the usual event handlers are called.
Alternatives considered
I considered a more similar approach to what we currently have for pointer devices. There's no
touchout
event like there is amouseout
event, but that can be fixed. On touch devices, you can only change an uncollapsed selection when there already is an uncollapsed selection (to get the handle bars). So we could enable the content editable wrapper "right in time" ontouchstart
whenever there is already an uncollapsed selection. However, I tried this multiple times and iOS seems to lose the selection whenever the content editable wrapper is turned on... The approach above is the only one that I could make work, and all e2e tests pass as well.Testing Instructions
This can more easily be tested with Playground. Open this link in Safari iOS and try to select multiple paragraphs.
Screenshots or screencast