prompts icon indicating copy to clipboard operation
prompts copied to clipboard

Allow selecting all options in a `multiselect`

Open duncanmcclean opened this issue 1 year ago • 6 comments

Currently, when using the multiselect component, if you want to select all of the available options, you have to manually hit space + down until you've toggled all the options.

This is mostly fine. However, if you have 10 options, you might not want to force your users to manually select all the options. Instead, you might want to let them toggle a single option.

This PR adds a new canSelectAll property to the MultiSelectPrompt class, which if true, will show an "All" option at the top of the options:

CleanShot 2024-05-10 at 17 34 25

multiselect(
    label: 'Select your favourite Laravel packages',
    options: [
        'laravel/prompts',
        'laravel/pulse',
        'laravel/horizon',
        'laravel/octane',
        'laravel/jetstream',
    ],
    canSelectAll: true,
);

When enabled, it'll select all the options and when disabled it'll deselect them all. In the case the user enables the "All" option but subsequently goes to select one of the options, the "All" option will be deselected.

An alternative UX for this could be to somehow disable all of the other options when the "All" option is toggled so they can't be changed.

Let me know if you'd like me to make any changes and I'll do my best to do them. Otherwise, if this isn't something you want to add to Prompts, then that's fine as well, just an idea I had. ❤️

duncanmcclean avatar May 10 '24 16:05 duncanmcclean

Hey @duncanmcclean,

This is pretty cool!

My only real issue is the "All" option is included in the returned array, which could cause problems depending on what the user is doing with the response. We'd need to be careful filtering it out, though, in case someone already has their own "All" option which they expect to be returned.

I don't love that it gets injected into the $options array, but I can't think of a nicer way off the top of my head. I was thinking it should be a renderer concern, but the prompt class needs to know when it has been toggled.

I think the new parameter should also accept a string, allowing folks to change or localize the wording (similar to the required param). I'm also not totally sold on the parameter naming, but I don't have a better alternative now.

jessarcher avatar May 13 '24 06:05 jessarcher

How about toggling when [Ctrl]+[A] is pressed?

macocci7 avatar May 14 '24 11:05 macocci7

My only real issue is the "All" option is included in the returned array, which could cause problems depending on what the user is doing with the response. We'd need to be careful filtering it out, though, in case someone already has their own "All" option which they expect to be returned. I don't love that it gets injected into the $options array, but I can't think of a nicer way off the top of my head. I was thinking it should be a renderer concern, but the prompt class needs to know when it has been toggled.

Ah yeah, that's a problem.

I can probably refactor the "All" option to live in the renderer instead, rather than being merged into the actual options of the prompt.

I think the new parameter should also accept a string, allowing folks to change or localize the wording (similar to the required param). I'm also not totally sold on the parameter naming, but I don't have a better alternative now.

Good idea 👍

I also don't completely love the name of the parameter but it's the best I could come up with at the time. I'll see if I can think of a better name.

How about toggling when [Ctrl]+[A] is pressed?

That's a good idea. In fact, I think I actually prefer it to what I've done in this PR in adding a separate option.

However, it seems like there's already a keybinding for ctrl + a to navigate to the top of the options list so maybe we can't do that 🤔

duncanmcclean avatar May 14 '24 11:05 duncanmcclean

@jessarcher

I've finally managed to carve out some time to work on refactoring this PR.

I've renamed the property to $selectAll (still don't totally love it but will do for now) and allowed for a string to be passed through.

I've also started to dive into moving the "select all" logic into the renderer, as suggested. However, I'm struggling to figure out the best way to prepend an option to the list without actually merging it into $this->options. 🤔

I'm likely missing something obvious, so if there's any pointers you can give me, that'd be great.

Like I mentioned in my previous comment, I actually quite like (or might even prefer) it being a keyboard shortcut, rather than a separate visual option. It would actually be very simple to implement (I built it in 5 minutes as a POC 😆 ). However, it looks like ctrl + a (the most likely candidate) has already been taken.

duncanmcclean avatar May 20 '24 21:05 duncanmcclean

Keyboard shortcuts can be funny in the terminal. Ctrl+A is a Readline/Emacs thing (added in #70).

I believe it technically only applies to typed input (i.e. horizontal movement), so I'm open to remapping it.

I'm thinking the following could be good:

  • Ctrl+A selects all in multiselect and also when focused on the options list in multisearch. Ctrl+E would do nothing in these contexts.
  • In a text input (including the text input of a multisearch) Ctrl+A and Ctrl+E move to the beginning and end of the input (i.e. unchanged)
  • Home and End would remain unchanged everywhere (moving between the top and bottom of lists and the beginning and end of text inputs).

I'm hoping this is pretty straight-forward and would also remove the need for a parameter to be added, as this feature would always be active.

jessarcher avatar May 21 '24 00:05 jessarcher

@jessarcher Thanks for your suggestions - I've updated this PR to use keyboard shortcuts, rather than the separate "All" option.

This PR is ready for review, whenever you're able.

duncanmcclean avatar May 23 '24 11:05 duncanmcclean

Thanks @duncanmcclean! I've made a few minor tweaks to the key ordering just to keep related things together in my head.

Just need to figure out the right approach for that multisearch behaviour. I can probably jump in if you need a hand.

jessarcher avatar May 24 '24 01:05 jessarcher

(I've fixed the phpstan issue in the main branch)

jessarcher avatar May 27 '24 00:05 jessarcher

There is an example in the documentation for a canSelectAll option here, but there is nothing in the code. When I googled I ended up here, but it seems like that option was reverted and replaced with a keyboard shortcut. So I guess that example should be deleted from docs then?

Great feature anyway!

pelmered avatar Aug 21 '24 07:08 pelmered