tty-prompt 0.18.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +95 -0
- data/README.md +598 -256
- data/lib/tty-prompt.rb +1 -2
- data/lib/tty/prompt.rb +192 -144
- data/lib/tty/prompt/answers_collector.rb +5 -5
- data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +20 -19
- data/lib/tty/prompt/choice.rb +9 -10
- data/lib/tty/prompt/choices.rb +30 -12
- data/lib/tty/prompt/confirm_question.rb +42 -16
- data/lib/tty/prompt/const.rb +17 -0
- data/lib/tty/prompt/converter_dsl.rb +6 -7
- data/lib/tty/prompt/converter_registry.rb +31 -26
- data/lib/tty/prompt/converters.rb +139 -32
- data/lib/tty/prompt/enum_list.rb +81 -26
- data/lib/tty/prompt/errors.rb +31 -0
- data/lib/tty/prompt/evaluator.rb +2 -2
- data/lib/tty/prompt/expander.rb +45 -15
- data/lib/tty/prompt/keypress.rb +33 -36
- data/lib/tty/prompt/list.rb +198 -63
- data/lib/tty/prompt/mask_question.rb +11 -8
- data/lib/tty/prompt/multi_list.rb +131 -28
- data/lib/tty/prompt/multiline.rb +9 -7
- data/lib/tty/prompt/paginator.rb +38 -26
- data/lib/tty/prompt/question.rb +92 -37
- data/lib/tty/prompt/question/checks.rb +20 -2
- data/lib/tty/prompt/question/modifier.rb +4 -2
- data/lib/tty/prompt/question/validation.rb +3 -3
- data/lib/tty/prompt/selected_choices.rb +77 -0
- data/lib/tty/prompt/slider.rb +125 -30
- data/lib/tty/prompt/statement.rb +3 -3
- data/lib/tty/prompt/suggestion.rb +7 -6
- data/lib/tty/prompt/symbols.rb +58 -34
- data/lib/tty/prompt/test.rb +36 -0
- data/lib/tty/prompt/timer.rb +75 -0
- data/lib/tty/prompt/utils.rb +1 -3
- data/lib/tty/prompt/version.rb +1 -1
- metadata +29 -227
- data/Rakefile +0 -8
- data/examples/ask.rb +0 -7
- data/examples/ask_valid.rb +0 -12
- data/examples/collect.rb +0 -21
- data/examples/echo.rb +0 -11
- data/examples/enum_select.rb +0 -7
- data/examples/enum_select_disabled.rb +0 -16
- data/examples/enum_select_paged.rb +0 -9
- data/examples/enum_select_wrapped.rb +0 -15
- data/examples/expand.rb +0 -29
- data/examples/in.rb +0 -9
- data/examples/inputs.rb +0 -10
- data/examples/key_events.rb +0 -15
- data/examples/keypress.rb +0 -9
- data/examples/mask.rb +0 -13
- data/examples/multi_select.rb +0 -8
- data/examples/multi_select_disabled.rb +0 -17
- data/examples/multi_select_paged.rb +0 -9
- data/examples/multi_select_wrapped.rb +0 -15
- data/examples/multiline.rb +0 -9
- data/examples/pause.rb +0 -9
- data/examples/select.rb +0 -24
- data/examples/select_disabled.rb +0 -18
- data/examples/select_enum.rb +0 -8
- data/examples/select_filtered.rb +0 -11
- data/examples/select_paginated.rb +0 -11
- data/examples/select_wrapped.rb +0 -15
- data/examples/slider.rb +0 -6
- data/examples/validation.rb +0 -9
- data/examples/yes_no.rb +0 -7
- data/lib/tty/prompt/messages.rb +0 -49
- data/lib/tty/prompt/timeout.rb +0 -78
- data/lib/tty/test_prompt.rb +0 -20
- data/spec/spec_helper.rb +0 -45
- data/spec/unit/ask_spec.rb +0 -132
- data/spec/unit/choice/eql_spec.rb +0 -22
- data/spec/unit/choice/from_spec.rb +0 -96
- data/spec/unit/choices/add_spec.rb +0 -12
- data/spec/unit/choices/each_spec.rb +0 -13
- data/spec/unit/choices/find_by_spec.rb +0 -10
- data/spec/unit/choices/new_spec.rb +0 -10
- data/spec/unit/choices/pluck_spec.rb +0 -9
- data/spec/unit/collect_spec.rb +0 -96
- data/spec/unit/converters/convert_bool_spec.rb +0 -58
- data/spec/unit/converters/convert_char_spec.rb +0 -11
- data/spec/unit/converters/convert_custom_spec.rb +0 -14
- data/spec/unit/converters/convert_date_spec.rb +0 -34
- data/spec/unit/converters/convert_file_spec.rb +0 -18
- data/spec/unit/converters/convert_number_spec.rb +0 -39
- data/spec/unit/converters/convert_path_spec.rb +0 -15
- data/spec/unit/converters/convert_range_spec.rb +0 -22
- data/spec/unit/converters/convert_regex_spec.rb +0 -12
- data/spec/unit/converters/convert_string_spec.rb +0 -21
- data/spec/unit/converters/on_error_spec.rb +0 -9
- data/spec/unit/distance/distance_spec.rb +0 -73
- data/spec/unit/enum_paginator_spec.rb +0 -75
- data/spec/unit/enum_select_spec.rb +0 -446
- data/spec/unit/error_spec.rb +0 -20
- data/spec/unit/evaluator_spec.rb +0 -67
- data/spec/unit/expand_spec.rb +0 -198
- data/spec/unit/keypress_spec.rb +0 -72
- data/spec/unit/mask_spec.rb +0 -132
- data/spec/unit/multi_select_spec.rb +0 -511
- data/spec/unit/multiline_spec.rb +0 -77
- data/spec/unit/new_spec.rb +0 -20
- data/spec/unit/ok_spec.rb +0 -10
- data/spec/unit/paginator_spec.rb +0 -73
- data/spec/unit/question/checks_spec.rb +0 -97
- data/spec/unit/question/default_spec.rb +0 -31
- data/spec/unit/question/echo_spec.rb +0 -38
- data/spec/unit/question/in_spec.rb +0 -115
- data/spec/unit/question/initialize_spec.rb +0 -12
- data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
- data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
- data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
- data/spec/unit/question/modify_spec.rb +0 -41
- data/spec/unit/question/required_spec.rb +0 -92
- data/spec/unit/question/validate_spec.rb +0 -115
- data/spec/unit/question/validation/call_spec.rb +0 -31
- data/spec/unit/question/validation/coerce_spec.rb +0 -30
- data/spec/unit/result_spec.rb +0 -40
- data/spec/unit/say_spec.rb +0 -67
- data/spec/unit/select_spec.rb +0 -660
- data/spec/unit/slider_spec.rb +0 -100
- data/spec/unit/statement/initialize_spec.rb +0 -15
- data/spec/unit/subscribe_spec.rb +0 -22
- data/spec/unit/suggest_spec.rb +0 -28
- data/spec/unit/warn_spec.rb +0 -21
- data/spec/unit/yes_no_spec.rb +0 -251
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-prompt.gemspec +0 -33
@@ -1,20 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative 'symbols'
|
3
|
+
require_relative "question"
|
5
4
|
|
6
5
|
module TTY
|
7
6
|
class Prompt
|
8
7
|
class MaskQuestion < Question
|
8
|
+
# Names for delete keys
|
9
|
+
DELETE_KEYS = %i[backspace delete].freeze
|
10
|
+
|
9
11
|
# Create masked question
|
10
12
|
#
|
11
13
|
# @param [Hash] options
|
12
14
|
# @option options [String] :mask
|
13
15
|
#
|
14
16
|
# @api public
|
15
|
-
def initialize(prompt, options
|
17
|
+
def initialize(prompt, **options)
|
16
18
|
super
|
17
|
-
@mask = options.fetch(:mask) {
|
19
|
+
@mask = options.fetch(:mask) { @prompt.symbols[:dot] }
|
18
20
|
@done_masked = false
|
19
21
|
@failure = false
|
20
22
|
end
|
@@ -28,19 +30,20 @@ module TTY
|
|
28
30
|
# @api public
|
29
31
|
def mask(char = (not_set = true))
|
30
32
|
return @mask if not_set
|
33
|
+
|
31
34
|
@mask = char
|
32
35
|
end
|
33
36
|
|
34
|
-
def keyreturn(
|
37
|
+
def keyreturn(_event)
|
35
38
|
@done_masked = true
|
36
39
|
end
|
37
40
|
|
38
|
-
def keyenter(
|
41
|
+
def keyenter(_event)
|
39
42
|
@done_masked = true
|
40
43
|
end
|
41
44
|
|
42
45
|
def keypress(event)
|
43
|
-
if
|
46
|
+
if DELETE_KEYS.include?(event.key.name)
|
44
47
|
@input.chop! unless @input.empty?
|
45
48
|
elsif event.value =~ /^[^\e\n\r]/
|
46
49
|
@input += event.value
|
@@ -76,7 +79,7 @@ module TTY
|
|
76
79
|
def read_input(question)
|
77
80
|
@done_masked = false
|
78
81
|
@failure = false
|
79
|
-
@input =
|
82
|
+
@input = ""
|
80
83
|
@prompt.print(question)
|
81
84
|
until @done_masked
|
82
85
|
@prompt.read_keypress
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "list"
|
4
|
+
require_relative "selected_choices"
|
4
5
|
|
5
6
|
module TTY
|
6
7
|
class Prompt
|
@@ -9,33 +10,83 @@ module TTY
|
|
9
10
|
#
|
10
11
|
# @api private
|
11
12
|
class MultiList < List
|
12
|
-
HELP = '(Use arrow%s keys, press Space to select and Enter to finish%s)'
|
13
|
-
|
14
13
|
# Create instance of TTY::Prompt::MultiList menu.
|
15
14
|
#
|
16
15
|
# @param [Prompt] :prompt
|
17
16
|
# @param [Hash] options
|
18
17
|
#
|
19
18
|
# @api public
|
20
|
-
def initialize(prompt, options)
|
19
|
+
def initialize(prompt, **options)
|
21
20
|
super
|
22
|
-
@selected =
|
21
|
+
@selected = SelectedChoices.new
|
23
22
|
@help = options[:help]
|
24
23
|
@echo = options.fetch(:echo, true)
|
24
|
+
@min = options[:min]
|
25
|
+
@max = options[:max]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set a minimum number of choices
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def min(value)
|
32
|
+
@min = value
|
25
33
|
end
|
26
34
|
|
35
|
+
# Set a maximum number of choices
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def max(value)
|
39
|
+
@max = value
|
40
|
+
end
|
41
|
+
|
42
|
+
# Callback fired when enter/return key is pressed
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def keyenter(*)
|
46
|
+
valid = true
|
47
|
+
valid = @min <= @selected.size if @min
|
48
|
+
valid = @selected.size <= @max if @max
|
49
|
+
|
50
|
+
super if valid
|
51
|
+
end
|
52
|
+
alias keyreturn keyenter
|
53
|
+
|
27
54
|
# Callback fired when space key is pressed
|
28
55
|
#
|
29
56
|
# @api private
|
30
57
|
def keyspace(*)
|
31
58
|
active_choice = choices[@active - 1]
|
32
59
|
if @selected.include?(active_choice)
|
33
|
-
@selected.
|
60
|
+
@selected.delete_at(@active - 1)
|
34
61
|
else
|
35
|
-
@selected
|
62
|
+
return if @max && @selected.size >= @max
|
63
|
+
|
64
|
+
@selected.insert(@active - 1, active_choice)
|
36
65
|
end
|
37
66
|
end
|
38
67
|
|
68
|
+
# Selects all choices when Ctrl+A is pressed
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
def keyctrl_a(*)
|
72
|
+
return if @max && @max < choices.size
|
73
|
+
|
74
|
+
@selected = SelectedChoices.new(choices.enabled, choices.enabled_indexes)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Revert currently selected choices when Ctrl+I is pressed
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def keyctrl_r(*)
|
81
|
+
return if @max && @max < choices.size
|
82
|
+
|
83
|
+
indexes = choices.each_with_index.reduce([]) do |acc, (choice, idx)|
|
84
|
+
acc << idx if !choice.disabled? && !@selected.include?(choice)
|
85
|
+
acc
|
86
|
+
end
|
87
|
+
@selected = SelectedChoices.new(choices.enabled - @selected.to_a, indexes)
|
88
|
+
end
|
89
|
+
|
39
90
|
private
|
40
91
|
|
41
92
|
# Setup default options and active selection
|
@@ -44,12 +95,23 @@ module TTY
|
|
44
95
|
def setup_defaults
|
45
96
|
validate_defaults
|
46
97
|
# At this stage, @choices matches all the visible choices.
|
47
|
-
|
98
|
+
default_indexes = @default.map do |d|
|
99
|
+
if d.to_s =~ INTEGER_MATCHER
|
100
|
+
d - 1
|
101
|
+
else
|
102
|
+
choices.index(choices.find_by(:name, d.to_s))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
@selected = SelectedChoices.new(@choices.values_at(*default_indexes),
|
106
|
+
default_indexes)
|
48
107
|
|
49
|
-
if
|
108
|
+
if @default.empty?
|
109
|
+
# no default, pick the first non-disabled choice
|
110
|
+
@active = choices.index { |choice| !choice.disabled? } + 1
|
111
|
+
elsif @default.last.to_s =~ INTEGER_MATCHER
|
50
112
|
@active = @default.last
|
51
|
-
|
52
|
-
@active =
|
113
|
+
elsif default_choice = choices.find_by(:name, @default.last.to_s)
|
114
|
+
@active = choices.index(default_choice) + 1
|
53
115
|
end
|
54
116
|
end
|
55
117
|
|
@@ -59,23 +121,63 @@ module TTY
|
|
59
121
|
#
|
60
122
|
# @api private
|
61
123
|
def selected_names
|
62
|
-
@selected.map(&:name).join(
|
124
|
+
@selected.map(&:name).join(", ")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Header part showing the minimum/maximum number of choices
|
128
|
+
#
|
129
|
+
# @return [String]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def minmax_help
|
133
|
+
help = []
|
134
|
+
help << "min. #{@min}" if @min
|
135
|
+
help << "max. #{@max}" if @max
|
136
|
+
"(%s) " % [help.join(", ")]
|
137
|
+
end
|
138
|
+
|
139
|
+
# Build a default help text
|
140
|
+
#
|
141
|
+
# @return [String]
|
142
|
+
#
|
143
|
+
# @api private
|
144
|
+
def default_help
|
145
|
+
str = []
|
146
|
+
str << "(Press "
|
147
|
+
str << "#{arrows_help} arrow"
|
148
|
+
str << " or 1-#{choices.size} number" if enumerate?
|
149
|
+
str << " to move, Space"
|
150
|
+
str << "/Ctrl+A|R" if @max.nil?
|
151
|
+
str << " to select"
|
152
|
+
str << " (all|rev)" if @max.nil?
|
153
|
+
str << (filterable? ? "," : " and")
|
154
|
+
str << " Enter to finish"
|
155
|
+
str << " and letters to filter" if filterable?
|
156
|
+
str << ")"
|
157
|
+
str.join
|
63
158
|
end
|
64
159
|
|
65
160
|
# Render initial help text and then currently selected choices
|
66
161
|
#
|
67
162
|
# @api private
|
68
163
|
def render_header
|
69
|
-
instructions = @prompt.decorate(help,
|
164
|
+
instructions = @prompt.decorate(help, @help_color)
|
165
|
+
minmax_suffix = @min || @max ? minmax_help : ""
|
166
|
+
print_selected = @selected.size.nonzero? && @echo
|
167
|
+
|
70
168
|
if @done && @echo
|
71
169
|
@prompt.decorate(selected_names, @active_color)
|
72
|
-
elsif @
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
170
|
+
elsif (@first_render && (help_start? || help_always?)) ||
|
171
|
+
(help_always? && !@filter.any? && !@done)
|
172
|
+
minmax_suffix +
|
173
|
+
(print_selected ? "#{selected_names} " : "") +
|
174
|
+
instructions
|
77
175
|
elsif filterable? && @filter.any?
|
78
|
-
|
176
|
+
minmax_suffix +
|
177
|
+
(print_selected ? "#{selected_names} " : "") +
|
178
|
+
@prompt.decorate(filter_help, @help_color)
|
179
|
+
else
|
180
|
+
minmax_suffix + (print_selected ? selected_names : "")
|
79
181
|
end
|
80
182
|
end
|
81
183
|
|
@@ -96,21 +198,22 @@ module TTY
|
|
96
198
|
def render_menu
|
97
199
|
output = []
|
98
200
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
indicator
|
201
|
+
sync_paginators if @paging_changed
|
202
|
+
paginator.paginate(choices, @active, @per_page) do |choice, index|
|
203
|
+
num = enumerate? ? (index + 1).to_s + @enum + " " : ""
|
204
|
+
indicator = (index + 1 == @active) ? @symbols[:marker] : " "
|
205
|
+
indicator += " "
|
103
206
|
message = if @selected.include?(choice) && !choice.disabled?
|
104
|
-
selected = @prompt.decorate(symbols[:radio_on], @active_color)
|
207
|
+
selected = @prompt.decorate(@symbols[:radio_on], @active_color)
|
105
208
|
"#{selected} #{num}#{choice.name}"
|
106
209
|
elsif choice.disabled?
|
107
|
-
@prompt.decorate(symbols[:cross], :red) +
|
210
|
+
@prompt.decorate(@symbols[:cross], :red) +
|
108
211
|
" #{num}#{choice.name} #{choice.disabled}"
|
109
212
|
else
|
110
|
-
"#{symbols[:radio_off]} #{num}#{choice.name}"
|
213
|
+
"#{@symbols[:radio_off]} #{num}#{choice.name}"
|
111
214
|
end
|
112
|
-
|
113
|
-
newline = (index ==
|
215
|
+
end_index = paginated? ? paginator.end_index : choices.size - 1
|
216
|
+
newline = (index == end_index) ? "" : "\n"
|
114
217
|
output << indicator + message + newline
|
115
218
|
end
|
116
219
|
|
data/lib/tty/prompt/multiline.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
3
|
+
require_relative "question"
|
4
|
+
require_relative "symbols"
|
5
5
|
|
6
6
|
module TTY
|
7
7
|
class Prompt
|
@@ -9,9 +9,9 @@ module TTY
|
|
9
9
|
#
|
10
10
|
# @api private
|
11
11
|
class Multiline < Question
|
12
|
-
HELP =
|
12
|
+
HELP = "(Press Ctrl+D or Ctrl+Z to finish)".freeze
|
13
13
|
|
14
|
-
def initialize(prompt, options
|
14
|
+
def initialize(prompt, **options)
|
15
15
|
super
|
16
16
|
@help = options[:help] || self.class::HELP
|
17
17
|
@first_render = true
|
@@ -25,6 +25,7 @@ module TTY
|
|
25
25
|
# @api public
|
26
26
|
def help(value = (not_set = true))
|
27
27
|
return @help if not_set
|
28
|
+
|
28
29
|
@help = value
|
29
30
|
end
|
30
31
|
|
@@ -42,7 +43,7 @@ module TTY
|
|
42
43
|
if !echo?
|
43
44
|
header
|
44
45
|
elsif @done
|
45
|
-
header << @prompt.decorate(
|
46
|
+
header << @prompt.decorate(@input.to_s, @active_color)
|
46
47
|
elsif @first_render
|
47
48
|
header << @prompt.decorate(help, @help_color)
|
48
49
|
@first_render = false
|
@@ -55,8 +56,9 @@ module TTY
|
|
55
56
|
@prompt.print(question)
|
56
57
|
@lines = read_input
|
57
58
|
@input = "#{@lines.first.strip} ..." unless @lines.first.to_s.empty?
|
58
|
-
if Utils.blank?(@input)
|
59
|
-
@input = default
|
59
|
+
if Utils.blank?(@input) && default?
|
60
|
+
@input = default
|
61
|
+
@lines = default
|
60
62
|
end
|
61
63
|
@evaluator.(@lines)
|
62
64
|
end
|
data/lib/tty/prompt/paginator.rb
CHANGED
@@ -5,23 +5,33 @@ module TTY
|
|
5
5
|
class Paginator
|
6
6
|
DEFAULT_PAGE_SIZE = 6
|
7
7
|
|
8
|
+
# The 0-based index of the first item on this page
|
9
|
+
attr_accessor :start_index
|
10
|
+
|
11
|
+
# The 0-based index of the last item on this page
|
12
|
+
attr_reader :end_index
|
13
|
+
|
14
|
+
# The 0-based index of the active item on this page
|
15
|
+
attr_reader :current_index
|
16
|
+
|
17
|
+
# The 0-based index of the previously active item on this page
|
18
|
+
attr_reader :last_index
|
19
|
+
|
8
20
|
# Create a Paginator
|
9
21
|
#
|
10
22
|
# @api private
|
11
|
-
def initialize(options
|
23
|
+
def initialize(**options)
|
12
24
|
@last_index = Array(options[:default]).flatten.first || 0
|
13
25
|
@per_page = options[:per_page]
|
14
|
-
@
|
26
|
+
@start_index = Array(options[:default]).flatten.first
|
15
27
|
end
|
16
28
|
|
17
|
-
#
|
29
|
+
# Reset current page indexes
|
18
30
|
#
|
19
|
-
# @
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
raise ArgumentError, 'no max index' unless @per_page
|
24
|
-
@lower_index + @per_page - 1
|
31
|
+
# @api private
|
32
|
+
def reset!
|
33
|
+
@start_index = nil
|
34
|
+
@end_index = nil
|
25
35
|
end
|
26
36
|
|
27
37
|
# Check if page size is valid
|
@@ -30,7 +40,7 @@ module TTY
|
|
30
40
|
#
|
31
41
|
# @api private
|
32
42
|
def check_page_size!
|
33
|
-
raise InvalidArgument,
|
43
|
+
raise InvalidArgument, "per_page must be > 0" if @per_page < 1
|
34
44
|
end
|
35
45
|
|
36
46
|
# Paginate collection given an active index
|
@@ -43,21 +53,21 @@ module TTY
|
|
43
53
|
# number of choice items per page
|
44
54
|
#
|
45
55
|
# @return [Enumerable]
|
56
|
+
# the list between start and end index
|
46
57
|
#
|
47
58
|
# @api public
|
48
59
|
def paginate(list, active, per_page = nil, &block)
|
49
60
|
current_index = active - 1
|
50
61
|
default_size = (list.size <= DEFAULT_PAGE_SIZE ? list.size : DEFAULT_PAGE_SIZE)
|
51
62
|
@per_page = @per_page || per_page || default_size
|
52
|
-
@lower_index ||= current_index
|
53
|
-
@upper_index ||= max_index
|
54
|
-
|
55
63
|
check_page_size!
|
64
|
+
@start_index ||= (current_index / @per_page) * @per_page
|
65
|
+
@end_index ||= @start_index + @per_page - 1
|
56
66
|
|
57
67
|
# Don't paginate short lists
|
58
68
|
if list.size <= @per_page
|
59
|
-
@
|
60
|
-
@
|
69
|
+
@start_index = 0
|
70
|
+
@end_index = list.size - 1
|
61
71
|
if block
|
62
72
|
return list.each_with_index(&block)
|
63
73
|
else
|
@@ -65,33 +75,35 @@ module TTY
|
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
78
|
+
step = (current_index - @last_index).abs
|
68
79
|
if current_index > @last_index # going up
|
69
|
-
if current_index
|
70
|
-
|
80
|
+
if current_index >= @end_index && current_index < list.size - 1
|
81
|
+
last_page = list.size - @per_page
|
82
|
+
@start_index = [@start_index + step, last_page].min
|
71
83
|
end
|
72
84
|
elsif current_index < @last_index # going down
|
73
|
-
if current_index
|
74
|
-
@
|
85
|
+
if current_index <= @start_index && current_index > 0
|
86
|
+
@start_index = [@start_index - step, 0].max
|
75
87
|
end
|
76
88
|
end
|
77
89
|
|
78
90
|
# Cycle list
|
79
91
|
if current_index.zero?
|
80
|
-
@
|
92
|
+
@start_index = 0
|
81
93
|
elsif current_index == list.size - 1
|
82
|
-
@
|
94
|
+
@start_index = list.size - 1 - (@per_page - 1)
|
83
95
|
end
|
84
96
|
|
85
|
-
@
|
97
|
+
@end_index = @start_index + (@per_page - 1)
|
86
98
|
@last_index = current_index
|
87
99
|
|
88
|
-
sliced_list = list[@
|
89
|
-
|
100
|
+
sliced_list = list[@start_index..@end_index]
|
101
|
+
page_range = (@start_index..@end_index)
|
90
102
|
|
91
|
-
return sliced_list.zip(
|
103
|
+
return sliced_list.zip(page_range).to_enum unless block_given?
|
92
104
|
|
93
105
|
sliced_list.each_with_index do |item, index|
|
94
|
-
block[item, @
|
106
|
+
block[item, @start_index + index]
|
95
107
|
end
|
96
108
|
end
|
97
109
|
end # Paginator
|