Skip to content
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

Add multi select #896

Merged
merged 11 commits into from
May 30, 2017
Merged

Add multi select #896

merged 11 commits into from
May 30, 2017

Conversation

ellatrix
Copy link
Member

See #62.

@ellatrix
Copy link
Member Author

More work to do there. Only adds a background.

@ellatrix
Copy link
Member Author

@jasmussen Feel free to style as you'd like. :)

@ellatrix
Copy link
Member Author

It's probably better to have a separate reducer for this...

@ellatrix ellatrix mentioned this pull request May 24, 2017
@ellatrix
Copy link
Member Author

To do:

  • Hide visible text selection.
  • Add block mover and delete button.

@aduth
Copy link
Member

aduth commented May 24, 2017

How will keyboard multi-selection work?

@ellatrix
Copy link
Member Author

ellatrix commented May 24, 2017

Good question. I thing we could add several ways. A good one would be the shortcut you normally use to select paragraph by paragraph, which is cmd+shift+arrow I believe.

@aduth
Copy link
Member

aduth commented May 24, 2017

I was expecting since I could click and drag across multiple blocks that I could shift+down from a paragraph into the next. Might be difficult to implement, since it's hard to know when shift+down occurs on the "last" line of a paragraph.

@ellatrix
Copy link
Member Author

@aduth You mean once you have a selection? If there's no selection yet, it will conflict with selecting line-by-line.

@ellatrix
Copy link
Member Author

Another thing we might want to do, in addition to drag, is a select mode, much like you have in the iOS Photos app. If it's toggled, clicking could select multiple blocks. The only awkward thing is that they'll be forced consecutive.

@ellatrix
Copy link
Member Author

Maybe @afercia has some more ideas.

@aduth
Copy link
Member

aduth commented May 24, 2017

This is what I mean: Repeatedly Shift+Down, I hit the end of the text block, but never moves into multi selection, which feels disjointed from the click+drag experience: https://cloudup.com/c-8wUVJ4iIv

@ellatrix
Copy link
Member Author

Aha! Yeah, I should be able to make that work. Again here a better selection state will be helpful. :)

@afercia
Copy link
Contributor

afercia commented May 24, 2017

If using repeatedly Shift+Down, how screen reader users would know when they've reached the end of a block? I'm not sure any kind of keyboard shortcut here would be ideal, since it will be confusing with what normally happens in a textarea. Really not sure what to do. The whole keyboard interaction for navigation and selection across blocks is a real issue, I have no great ideas so far.

@jasmussen
Copy link
Contributor

jasmussen commented May 25, 2017

Looks good, works pretty well, nice work! I added a slightly darker border around each selected block.

Hide visible text selection.

Can we do this by simply styling the selection rectangle the same color as the background, when you have multi-selected? Seems like it would be good to only hide the text selection, not actually remove it. We might find it annoying if you're just trying to select text inside a block, and accidentally invoke the multi block selection thing to ony have your selection undone.

In that vein it might be good to have some buffer zone or timer, so that you only actually start selecting across blocks once your mouse is, like, n pixels over the next block, and only then after half a second has passed. Something in that vein would be good to experiment with.

Right now you can't deselect. Should we make it so clicking outside, just like how you deselect text, deselects blocks too? Escape should probably also deselect.

How will keyboard multi-selection work?

Maybe ⌘A, when focus is outside of a block, selects all blocks. But maybe you can't select just a few at all, using just the keyboard.

The purpose of multi selection is to make it easy to delete multiple blocks at once, or move multiple blocks at once. You will already be able to do this using keyboard shortcuts alone — simply invoke the keyboard shortcuts for delete blocks multiple times in a row. Or tab to the block you want to delete, then delete it. Same with rearranging blocks.

That is — you can accomplish the same end result using keyboard shortcuts. It's just a slightly different flow. This is similar to how layouting apps like Keynote or Sketch work.

@ellatrix
Copy link
Member Author

ellatrix commented May 26, 2017

Thanks for adding the styling! I saw you design also had some spacing in between, is this something you'd like to see too?

Seems like it would be good to only hide the text selection, not actually remove it.

I suggested hiding right? :)

I'll have a look at the buffering and deselect.

@ellatrix ellatrix force-pushed the add/multi-select branch 2 times, most recently from 8b3e6ff to aa092eb Compare May 27, 2017 09:07
@ellatrix
Copy link
Member Author

ellatrix commented May 27, 2017

Right now you can't deselect.

For me deselecting works by clicking on any of the blocks.

@ellatrix
Copy link
Member Author

ellatrix commented May 28, 2017

I've added the delete and moving buttons. Still needs some tests. Also the location of these buttons may not be ideal.

@ellatrix ellatrix requested a review from aduth May 29, 2017 12:00
@ellatrix ellatrix added this to the May 29 to Jun 2 milestone May 29, 2017
@mtias mtias added [Feature] Blocks Overall functionality of blocks General Interface Parts of the UI which don't fall neatly under other labels. labels May 29, 2017
Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Realy awesome job here 👏🏻

I'm seeing some minor issues:

  • Removing using backspace is not working (The "regular" backspace event is being handled instead.

  • Removing using the button don't work sometimes. I'm not sure when it works and when it doesn't

  • The sticky behaviour (remove button) is not working as expected.

onSelectionStart={ () => this.onSelectionStart( uid ) }
onSelectionChange={ () => this.onSelectionChange( uid ) }
onSelectionEnd={ this.onSelectionEnd }
selectedBlocks={ selectedBlocks }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we drop the selectedProps prop and move it to the connect of VisualEditorBlock instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah :)

@@ -229,7 +241,26 @@ class VisualEditorBlock extends wp.element.Component {
</div>
</CSSTransitionGroup>
}
<div onKeyPress={ this.maybeStartTyping }>
{ this.props.isFirstSelected && (
Copy link
Contributor

@youknowriad youknowriad May 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Could we destructure all the this.props.*?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I think I forgot to remove this after I saw it clashes with the selector... Any naming conventions here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok keeping it to avoid conflicting with the selector. but Maybe I'd rename the selector isFirstSelectedBlock

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

switch ( action.type ) {
case 'RESET_BLOCKS':
return action.blocks.map( ( { uid } ) => uid );

case 'INSERT_BLOCK':
case 'INSERT_BLOCK': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious to know why do you wrap case between {}. Is this a good practice I'm missing?

Copy link
Member Author

@ellatrix ellatrix May 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'd rather scope the const declarations to one case instead of the whole reducer. You can use {} anywhere you want to create a new block scope AFAIK.

/* ... */

{
    let something = 1;
}

/* ... */

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, Thanks for the explanation

@ellatrix
Copy link
Member Author

ellatrix commented May 29, 2017

@youknowriad Yep, I know the sticky positioning is not working... I'm not sure how to resolve this atm. Was thinking to do try to address this in a separate PR. I'll add the backspace shortcut.

@ellatrix
Copy link
Member Author

ellatrix commented May 29, 2017

Regarding sticky positioning... We could wrap the selected block in an extra div. This would also be far cleaner than adding the buttons to the first selected block. A big downside is that iframes would reload. The other solution is adding the buttons to the parent division, but we'll have to reposition it with JS.

@ellatrix
Copy link
Member Author

Something else that just popped in my mind: overlay the selected area with a div than contains the buttons and can be clicked through. Downside here is that the width and height may easily get out of sync.

@jasmussen
Copy link
Contributor

This feels pretty good, especially as a first PR version of this. Click outside to deselect works great, as does the text selection trick you're doing. Great work!

I can't get the mover to work.

The sticky toolbar and cross-select "buffer zone" seem like perfect candidates to address separately.

On Sticky — would it make sense to think of a "global toolbar" component, which can be invoked in situations like this? I could imagine us using this one for mobile improvements as well. That is, a sticky toolbar that sits where it needs to sit in the DOM in order to scroll the full length of the document, but is only used as a tray for buttons in certain situations?

The pattern to emulate could be that of Google Photos, where you have one toolbar when you don't have any photos selected:

photos

... and another toolbar replacing it, when you have photos selected:

photos selected

Might be nice to even copy the "8 blocks selected" pattern. This could even be the location of the mover buttons.

Thanks for adding the styling! I saw you design also had some spacing in between, is this something you'd like to see too?

Not critical, and not worth doing in this PR. The truth is right now the blocks are flush next to each other, so that's why there isn't that space. However if we remove one px of vertical padding inside the blocks, and make that margin between them instead, it should address that. But because of collapsing margins and such, worth doing separately.

@ellatrix
Copy link
Member Author

I agree that we should iterate on the rest of the issues. I'd like to get it merged ASAP in this state. :) The block movers were working for me be let me double check. :)

@ellatrix
Copy link
Member Author

Huh, I broke them...

@ellatrix
Copy link
Member Author

@jasmussen Actually no, they work fine here. I'm not sure why it's not working for you.

@ellatrix
Copy link
Member Author

Sorry, it's the last commit, I see it now. Got confused between what I had checked out. :/

@jasmussen
Copy link
Contributor

Moving seems to work now! It doesn't scroll you to the new location like moving an individual block does, though. Is this fixable? If yes, fine to do in a separate PR.

@ellatrix
Copy link
Member Author

Yeah I knew it wouldn't scroll. I'll quickly have a look how much it takes to make it work.

@jasmussen
Copy link
Contributor

Impressive! Moving works great now! 💖

@ellatrix ellatrix requested a review from youknowriad May 30, 2017 12:35
@ellatrix
Copy link
Member Author

Any technical concerns? Otherwise I'll merge in the next few hours.

@ellatrix ellatrix merged commit a14d015 into master May 30, 2017
@ellatrix ellatrix deleted the add/multi-select branch May 30, 2017 16:33
}

componentDidMount() {
if ( this.props.focus ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain the issues with focus you were seeing? Do we need to monitor this prop if it changes during the lifetime of the component?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain the issues with focus you were seeing? Do we need to monitor this prop if it changes during the lifetime of the component?

Circling back to this. I encountered the Toolbar focus prop when working on some unrelated refactoring and had no idea what its purpose is. I still don't, and could use some clarification as to this unanswered question.

@@ -10,11 +10,11 @@ import './style.scss';
import Button from '../button';
import Dashicon from '../dashicon';

function IconButton( { icon, children, label, className, ...additionalProps } ) {
function IconButton( { icon, children, label, className, focus, ...additionalProps } ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

focus would be passed to the underlying Button without being explicitly handled here, based on the behavior of additionalProps spread.

@@ -30,6 +30,7 @@ function Toolbar( { controls } ) {
'is-active': control.isActive,
} ) }
aria-pressed={ control.isActive }
focus={ focus && ! index }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is causing some trouble for me in tests. I am unsure as to what is happening here. Where is focus coming from? Is this supposed to be passed into the component as a prop? If that is the case, we will need to add focus to the function signature at the top. Toolbar( { controls, focus } ).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. This was causing a bug too. Thanks for catching it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Blocks Overall functionality of blocks General Interface Parts of the UI which don't fall neatly under other labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants