class TTY::Prompt::Expander

@api private
Used by {Prompt} to display key options question.
A class responsible for rendering expanding options

def call(message, possibilities, &block)

Other tags:
    Api: - public
def call(message, possibilities, &block)
  choices(possibilities)
  @message = message
  block.call(self) if block
  setup_defaults
  choice(HELP_CHOICE)
  render
end

def choice(value, &block)

Other tags:
    Api: - public
def choice(value, &block)
  if block
    @choices << value.update(value: block)
  else
    @choices << value
  end
end

def choices(values)

Other tags:
    Api: - public

Parameters:
  • values (Array[Object]) --
def choices(values)
  values.each { |val| choice(val) }
end

def collapsed?

def collapsed?
  @status == :collapsed
end

def count_lines

Other tags:
    Api: - private
def count_lines
  lines = render_header.scan("\n").length + 1
  if @hint
    lines += @hint.scan("\n").length + 1
  elsif expanded?
    lines += @choices.length
    lines += render_footer.scan("\n").length + 1
  end
  lines
end

def default(value = (not_set = true))

Other tags:
    Api: - public
def default(value = (not_set = true))
  return @default if not_set
  @default = value
end

def expand

def expand
  @status = :expanded
end

def expanded?

def expanded?
  @status == :expanded
end

def initialize(prompt, options = {})

Other tags:
    Api: - public
def initialize(prompt, options = {})
  @prompt       = prompt
  @prefix       = options.fetch(:prefix) { @prompt.prefix }
  @default      = options.fetch(:default) { 1 }
  @active_color = options.fetch(:active_color) { @prompt.active_color }
  @help_color   = options.fetch(:help_color) { @prompt.help_color }
  @choices      = Choices.new
  @selected     = nil
  @done         = false
  @status       = :collapsed
  @hint         = nil
  @default_key  = false
  @prompt.subscribe(self)
end

def keyenter(_)

Other tags:
    Api: - public
def keyenter(_)
  if @input.nil? || @input.empty?
    @input = @choices[@default - 1].key
    @default_key = true
  end
  selected = select_choice(@input)
  if selected && selected.key.to_s == 'h'
    expand
    @selected = nil
    @input = ''
  elsif selected
    @done = true
    @selected = selected
  else
    @input = ''
  end
end

def keypress(event)

Other tags:
    Api: - public
def keypress(event)
  if [:backspace, :delete].include?(event.key.name)
    @input.chop! unless @input.empty?
  elsif event.value =~ /^[^\e\n\r]/
    @input += event.value
  end
  @selected = select_choice(@input)
  if @selected && !@default_key && collapsed?
    @hint = @selected.name
  end
end

def possible_keys

Other tags:
    Api: - private

Returns:
  • (String) -
def possible_keys
  keys = @choices.pluck(:key)
  default_key = keys[@default - 1]
  if @selected
    index = keys.index(@selected.key)
    keys[index] = @prompt.decorate(keys[index], @active_color)
  elsif @input.to_s.empty? && default_key
    keys[@default - 1] = @prompt.decorate(default_key, @active_color)
  end
  keys.join(',')
end

def read_input

def read_input
  @prompt.read_keypress
end

def refresh

Other tags:
    Api: - private
def refresh
  lines = count_lines
  if @hint && (!@selected || @done)
    @hint = nil
    @prompt.print(@prompt.clear_lines(lines, :down))
    @prompt.print(@prompt.cursor.prev_line)
  elsif expanded?
    @prompt.print(@prompt.clear_lines(lines))
  else
    @prompt.print(@prompt.clear_line)
  end
end

def render

Other tags:
    Api: - private
def render
  @input = ''
  until @done
    render_question
    read_input
    refresh
  end
  render_question
  render_answer
end

def render_answer

Other tags:
    Api: - private
def render_answer
  @selected.value
end

def render_footer

def render_footer
  "  Choice [#{@choices[@default - 1].key}]: #{@input}"
end

def render_header

def render_header
  header = "#{@prefix}#{@message} "
  if @done
    selected_item = "#{@selected.name}"
    header << @prompt.decorate(selected_item, @active_color)
  elsif collapsed?
    header << %[(enter "h" for help) ]
    header << "[#{possible_keys}] "
    header << @input
  end
  header
end

def render_hint

Other tags:
    Api: - private
def render_hint
  hint = "\n"
  hint << @prompt.decorate('>> ', @active_color)
  hint << @hint
  @prompt.print(hint)
  @prompt.print(@prompt.cursor.prev_line)
  @prompt.print(@prompt.cursor.forward(@prompt.strip(render_header).size))
end

def render_menu

Other tags:
    Api: - private
def render_menu
  output = "\n"
  @choices.each do |choice|
    chosen = %(#{choice.key} - #{choice.name})
    if @selected && @selected.key == choice.key
      chosen = @prompt.decorate(chosen, @active_color)
    end
    output << '  ' + chosen + "\n"
  end
  output
end

def render_question

Other tags:
    Api: - private
def render_question
  header = render_header
  @prompt.print(header)
  render_hint if @hint
  @prompt.print("\n") if @done
  if !@done && expanded?
    @prompt.print(render_menu)
    @prompt.print(render_footer)
  end
end

def select_choice(key)

Other tags:
    Api: - private

Returns:
  • (Choice) -
def select_choice(key)
  @choices.find_by(:key, key)
end

def setup_defaults

def setup_defaults
  validate_choices
end

def validate_choices

def validate_choices
  errors = []
  keys = []
  @choices.each do |choice|
    if choice.key.nil?
      errors <<  "Choice #{choice.name} is missing a :key attribute"
      next
    end
    if choice.key.length != 1
      errors << "Choice key `#{choice.key}` is more than one character long."
    end
    if choice.key.to_s == 'h'
      errors << "Choice key `#{choice.key}` is reserved for help menu."
    end
    if keys.include?(choice.key)
      errors << "Choice key `#{choice.key}` is a duplicate."
    end
    keys << choice.key if choice.key
  end
  errors.each { |err| fail ConfigurationError, err }
end