diff --git a/README.md b/README.md index ba95188..27d62f4 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Or install it yourself as: * [2.6.3.8 :min](#2638-min) * [2.6.3.9 :max](#2639-max) * [2.6.3.10 :submit_keys](#26310-submit_keys) - * [2.6.3.11 :select_key](#262311-select_key) + * [2.6.3.11 :select_keys](#262311-select_keys) * [2.6.4 enum_select](#264-enum_select) * [2.6.4.1 :per_page](#2641-per_page) * [2.6.4.1 :disabled](#2641-disabled) @@ -1239,14 +1239,14 @@ The user can then press the `Ctrl+S` key to confirm their selection: Please note that alphanumeric keys are *not* supported. -#### 2.6.3.11 `:select_key` +#### 2.6.3.11 `:select_keys` You can configure which key selects an option (`:space` default). This is particularly useful in conjunction with the `filter` option, as you may have choices that include spaces. ```ruby choices = ["gin", "gin tonic", "gin fizz", "beer"] -prompt.multi_select("Select drinks?", choices, filter: true, select_key: :ctrl_s) +prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [:ctrl_s]) # => # Select drinks? (Press ↑/↓ arrow keys to move, Ctrl+S/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) # ‣ ⬡ gin @@ -1282,6 +1282,19 @@ The user can then press the `Ctrl+S` key combo to select the options: # ⬡ gin fizz ``` +Similar to the `:submit_keys` option, you may also pass your own key labels to be displayed in the hint: + +```ruby +choices = ["gin", "gin tonic", "gin fizz", "beer"] +prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctr_s: "Ctrl-S"}, {space: "Spacebar"}]) +# => +# Select drinks? (Press ↑/↓ arrow keys to move, Ctrl-S or Spacebar/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) +# ‣ ⬡ gin +# ⬡ gin tonic +# ⬡ gin fizz +# ⬡ beer +``` + Please note that alphanumeric keys are *not* supported. ### 2.6.4 enum_select diff --git a/examples/multi_select_custom_keys.rb b/examples/multi_select_custom_keys.rb new file mode 100644 index 0000000..ad12b9c --- /dev/null +++ b/examples/multi_select_custom_keys.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require_relative "../lib/tty-prompt" + +prompt = TTY::Prompt.new + +drinks = %w[vodka beer wine whisky bourbon] +prompt.multi_select("Choose your favourite drink?", drinks, + submit_keys: [:return, {escape: "Esc"}], + select_keys: [{space: "Spacebar"}, :ctrl_s]) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 0866ea0..f2c82dc 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -205,8 +205,8 @@ def key_help_label(key_name) # @return [String] # # @api private - def submit_keys_help - labels = @submit_keys.values.uniq + def keys_help(keys) + labels = keys.values.uniq if labels.length == 1 labels[0] else @@ -226,7 +226,7 @@ def default_help str << " or 1-#{choices.size} number" if enumerate? str << " to move" str << (filterable? ? "," : " and") - str << " #{submit_keys_help} to select" + str << " #{keys_help(@submit_keys)} to select" str << " and letters to filter" if filterable? str << ")" str.join diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 9e162d6..8ffd805 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -19,7 +19,7 @@ class MultiList < List def initialize(prompt, **options) super @selected = SelectedChoices.new - @select_key = options.fetch(:select_key) { :space } + @select_keys = keys_with_labels(options.fetch(:select_keys, [:space])) @help = options[:help] @echo = options.fetch(:echo, true) @min = options[:min] @@ -78,7 +78,7 @@ def select_choice # # @api private def keypress(event) - if event.key.name == @select_key + if @select_keys.include?(event.key.name) select_choice else super(event) @@ -113,11 +113,11 @@ def keyctrl_r(*) # # @api private def check_clashing_keys - return unless @submit_keys.include?(@select_key) + return if (@submit_keys.keys & @select_keys.keys).empty? raise ConfigurationError, ":submit_keys #{@submit_keys.keys} are clashing with " \ - ":select_key (:#{@select_key})" + ":select_keys #{@select_keys.keys}" end # Setup default options and active selection @@ -177,12 +177,12 @@ def default_help str << "(Press " str << "#{arrows_help} arrow" str << " or 1-#{choices.size} number" if enumerate? - str << " to move, #{key_help_label(@select_key)}" + str << " to move, #{keys_help(@select_keys)}" str << "/Ctrl+A|R" if @max.nil? str << " to select" str << " (all|rev)" if @max.nil? str << (filterable? ? "," : " and") - str << " #{submit_keys_help} to finish" + str << " #{keys_help(@submit_keys)} to finish" str << " and letters to filter" if filterable? str << ")" str.join diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index 1e6a4eb..e369863 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -84,15 +84,30 @@ def exit_message(prompt, choices) expect(prompt.output.string).to eq(expected_output) end - it "selects item when custom key pressed" do + it "selects item when custom key pressed and shows custom key labels" do choices = %w[vodka beer wine whisky bourbon] prompt.input << "\C-s\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, select_key: :ctrl_s)).to eq(["vodka"]) + expect(prompt.multi_select("Select drinks?", choices, select_keys: [:ctrl_s, {escape: "Esc"}])).to eq(["vodka"]) expected_output = output_helper("Select drinks?", choices, "vodka", [], init: true, - hint: "Press #{up_down} arrow to move, Ctrl+S/Ctrl+A|R to select (all|rev) and Enter to finish") + + hint: "Press #{up_down} arrow to move, Ctrl+S or Esc/Ctrl+A|R to select (all|rev) and Enter to finish") + + output_helper("Select drinks?", choices, "vodka", ["vodka"]) + + exit_message("Select drinks?", %w[vodka]) + + expect(prompt.output.string).to eq(expected_output) + end + + it "selects item and submits selection with custom keys" do + choices = %w[vodka beer wine whisky bourbon] + prompt.input << "\C-s\e" + prompt.input.rewind + expect(prompt.multi_select("Select drinks?", choices, select_keys: [:ctrl_s], submit_keys: [{escape: "Esc"}])).to eq(["vodka"]) + + expected_output = + output_helper("Select drinks?", choices, "vodka", [], init: true, + hint: "Press #{up_down} arrow to move, Ctrl+S/Ctrl+A|R to select (all|rev) and Esc to finish") + output_helper("Select drinks?", choices, "vodka", ["vodka"]) + exit_message("Select drinks?", %w[vodka]) @@ -272,16 +287,16 @@ def exit_message(prompt, choices) expect { prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space] are clashing with :select_key (:space)") + ":submit_keys [:space] are clashing with :select_keys [:space]") end it "raises error when submit and select keys clash (configured)" do prompt.input << "\r" prompt.input.rewind expect { - prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_key: :space) + prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space, :ctrl_s] are clashing with :select_key (:space)") + ":submit_keys [:space, :ctrl_s] are clashing with :select_keys [:space]") end @@ -756,7 +771,7 @@ def exit_message(prompt, choices) prompt.input << "\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, filter: true, select_key: :ctrl_s)).to eq(["gin fizz", "gin tonic"]) + expect(prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [:ctrl_s])).to eq(["gin fizz", "gin tonic"]) expected_output = output_helper("Select drinks?", choices, "gin", [], init: true,