Module: Clack::Core::OptionsHelper

Included in:
Prompts::Autocomplete, Prompts::AutocompleteMultiselect, Prompts::Multiselect, Prompts::Path, Prompts::Select
Defined in:
lib/clack/core/options_helper.rb

Overview

Shared functionality for option-based prompts (Select, Multiselect, Autocomplete, etc.). Handles option normalization, cursor navigation, and scrolling.

Including classes must define:

  • @max_items [Integer, nil] maximum visible items (nil = show all)

  • @option_index [Integer] current selection index

  • @scroll_offset [Integer] current scroll position

Including classes must implement:

  • navigable_items [Array] returns the current list to navigate

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.normalize_option(opt) ⇒ Option

Normalize a single option to an Option value object.

Parameters:

  • opt (Hash, String, Symbol)

    Raw option

Returns:

  • (Option)

    Normalized option



56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/clack/core/options_helper.rb', line 56

def self.normalize_option(opt)
  case opt
  when Hash
    Option.new(
      value: opt[:value],
      label: opt[:label] || opt[:value].to_s,
      hint: opt[:hint],
      disabled: opt[:disabled] || false
    )
  else
    Option.new(value: opt, label: opt.to_s, hint: nil, disabled: false)
  end
end

Instance Method Details

#find_initial_cursor(initial_value) ⇒ Integer

Find initial cursor position based on initial value or first enabled option.

Parameters:

  • initial_value (Object, nil)

    Initial value to select

Returns:

  • (Integer)

    Cursor position



142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/clack/core/options_helper.rb', line 142

def find_initial_cursor(initial_value)
  items = navigable_items
  return 0 if items.empty?

  if initial_value.nil?
    # Start at first enabled option
    return items[0].disabled ? first_enabled_index : 0
  end

  idx = items.find_index { |o| o.value == initial_value }
  (idx && !items[idx].disabled) ? idx : first_enabled_index
end

#find_next_enabled(from, delta) ⇒ Integer

Find the next enabled option in the given direction. Wraps around the list if necessary.

Parameters:

  • from (Integer)

    Starting index

  • delta (Integer)

    Direction (+1 for forward, -1 for backward)

Returns:

  • (Integer)

    Index of next enabled option, or from if all disabled



76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/clack/core/options_helper.rb', line 76

def find_next_enabled(from, delta)
  items = navigable_items
  max = items.length
  idx = (from + delta) % max

  max.times do
    return idx unless items[idx].disabled

    idx = (idx + delta) % max
  end

  from
end

#first_enabled_indexInteger

Index of the first enabled option.

Returns:

  • (Integer)


92
93
94
# File 'lib/clack/core/options_helper.rb', line 92

def first_enabled_index
  find_next_enabled(-1, 1)
end

#move_cursor(delta) ⇒ Object

Move option_index in the given direction, skipping disabled options.

Parameters:

  • delta (Integer)

    Direction (+1 for down/right, -1 for up/left)



99
100
101
102
# File 'lib/clack/core/options_helper.rb', line 99

def move_cursor(delta)
  @option_index = find_next_enabled(@option_index, delta)
  update_scroll
end

#move_selection(delta) ⇒ Object

Move the selection index by delta, wrapping around. Unlike move_cursor, does not skip disabled items.

Parameters:

  • delta (Integer)

    direction (+1 for down, -1 for up)



108
109
110
111
112
113
114
# File 'lib/clack/core/options_helper.rb', line 108

def move_selection(delta)
  items = navigable_items
  return if items.empty?

  @option_index = (@option_index + delta) % items.length
  update_scroll
end

The list of items to navigate. Override in subclasses that use a filtered or dynamic list (e.g., Autocomplete uses @filtered).

Returns:

  • (Array)


158
159
160
# File 'lib/clack/core/options_helper.rb', line 158

def navigable_items
  @options
end

#normalize_options(options) ⇒ Array<Hash>

Normalize options to a consistent hash format. Accepts strings, symbols, or hashes with value/label/hint/disabled keys.

Parameters:

  • options (Array)

    Raw options in various formats

Returns:

  • (Array<Hash>)

    Normalized option hashes

Raises:

  • (ArgumentError)

    if options is empty



47
48
49
50
51
# File 'lib/clack/core/options_helper.rb', line 47

def normalize_options(options)
  raise ArgumentError, "options cannot be empty" if options.nil? || options.empty?

  options.map { |opt| OptionsHelper.normalize_option(opt) }
end

#update_scrollObject

Update scroll offset to keep option_index visible within the window.



127
128
129
130
131
132
133
134
135
136
# File 'lib/clack/core/options_helper.rb', line 127

def update_scroll
  items = navigable_items
  return unless @max_items && items.length > @max_items

  if @option_index < @scroll_offset
    @scroll_offset = @option_index
  elsif @option_index >= @scroll_offset + @max_items
    @scroll_offset = @option_index - @max_items + 1
  end
end

#visible_optionsArray<Hash>

Get the currently visible options based on scroll offset and max_items.

Returns:

  • (Array<Hash>)

    Visible options



119
120
121
122
123
124
# File 'lib/clack/core/options_helper.rb', line 119

def visible_options
  items = navigable_items
  return items unless @max_items && items.length > @max_items

  items[@scroll_offset, @max_items]
end