natty-ui 0.35.0 → 1.0.2

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -6
  3. data/examples/24bit-colors.rb +9 -5
  4. data/examples/3bit-colors.rb +7 -7
  5. data/examples/8bit-colors.rb +5 -7
  6. data/examples/attributes.rb +2 -3
  7. data/examples/elements.rb +9 -6
  8. data/examples/examples.rb +9 -9
  9. data/examples/frames.rb +31 -0
  10. data/examples/hbars.rb +6 -3
  11. data/examples/info.rb +13 -10
  12. data/examples/key-codes.rb +8 -9
  13. data/examples/ls.rb +24 -22
  14. data/examples/named-colors.rb +4 -3
  15. data/examples/sections.rb +26 -24
  16. data/examples/select.rb +28 -0
  17. data/examples/sh.rb +25 -7
  18. data/examples/tables.rb +19 -37
  19. data/examples/tasks.rb +32 -22
  20. data/examples/vbars.rb +5 -3
  21. data/lib/natty-ui/dumb_progress.rb +68 -0
  22. data/lib/natty-ui/element.rb +61 -70
  23. data/lib/natty-ui/features.rb +771 -949
  24. data/lib/natty-ui/frame.rb +87 -0
  25. data/lib/natty-ui/helper/table.rb +1376 -0
  26. data/lib/natty-ui/margin.rb +83 -0
  27. data/lib/natty-ui/progress.rb +116 -152
  28. data/lib/natty-ui/renderer/bars.rb +93 -0
  29. data/lib/natty-ui/renderer/choice.rb +56 -0
  30. data/lib/natty-ui/renderer/dumb_choice.rb +34 -0
  31. data/lib/natty-ui/renderer/dumb_select.rb +60 -0
  32. data/lib/natty-ui/renderer/dumb_shell_runner.rb +19 -0
  33. data/lib/natty-ui/renderer/heading.rb +26 -0
  34. data/lib/natty-ui/renderer/horizontal_rule.rb +32 -0
  35. data/lib/natty-ui/{ls_renderer.rb → renderer/ls.rb} +15 -27
  36. data/lib/natty-ui/renderer/mark.rb +13 -0
  37. data/lib/natty-ui/renderer/quote.rb +13 -0
  38. data/lib/natty-ui/renderer/select.rb +63 -0
  39. data/lib/natty-ui/renderer/shell.rb +15 -0
  40. data/lib/natty-ui/renderer/shell_runner.rb +29 -0
  41. data/lib/natty-ui/renderer/table_renderer.rb +429 -0
  42. data/lib/natty-ui/section.rb +144 -32
  43. data/lib/natty-ui/task.rb +38 -25
  44. data/lib/natty-ui/temporary.rb +27 -14
  45. data/lib/natty-ui/utils/border.rb +139 -0
  46. data/lib/natty-ui/utils/str_const.rb +62 -0
  47. data/lib/natty-ui/utils/utils.rb +47 -0
  48. data/lib/natty-ui/version.rb +1 -1
  49. data/lib/natty-ui.rb +76 -35
  50. metadata +31 -28
  51. data/examples/cols.rb +0 -38
  52. data/examples/illustration.rb +0 -60
  53. data/examples/options.rb +0 -28
  54. data/examples/themes.rb +0 -51
  55. data/lib/natty-ui/attributes.rb +0 -593
  56. data/lib/natty-ui/choice.rb +0 -67
  57. data/lib/natty-ui/dumb_choice.rb +0 -47
  58. data/lib/natty-ui/dumb_options.rb +0 -64
  59. data/lib/natty-ui/framed.rb +0 -50
  60. data/lib/natty-ui/hbars_renderer.rb +0 -66
  61. data/lib/natty-ui/options.rb +0 -78
  62. data/lib/natty-ui/shell_renderer.rb +0 -91
  63. data/lib/natty-ui/table.rb +0 -325
  64. data/lib/natty-ui/table_renderer.rb +0 -165
  65. data/lib/natty-ui/theme.rb +0 -403
  66. data/lib/natty-ui/utils.rb +0 -111
  67. data/lib/natty-ui/vbars_renderer.rb +0 -49
  68. data/lib/natty-ui/width_finder.rb +0 -137
  69. data/natty-ui.gemspec +0 -34
data/examples/tables.rb CHANGED
@@ -2,57 +2,39 @@
2
2
 
3
3
  require_relative '../lib/natty-ui'
4
4
 
5
- ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Print Tables[/]' do
5
+ ui.section '[b]ᓚᕠᗢ NattyUI[/b] [i green]Print Tables' do
6
6
  ui.space
7
- ui.table(border: :rounded, border_around: true) do |table|
8
- table.add(
9
- 'Header Col 0',
10
- 'Header Col 1',
11
- 'Header Col 2',
12
- align: :centered,
13
- style: %i[bold red]
7
+ ui.table border_frame: :double do |table|
8
+ table.add_row(
9
+ '[bold red]Header Col 0',
10
+ '[bold red]Header Col 1',
11
+ '[bold red]Header Col 2',
12
+ align: :center
14
13
  )
15
14
 
16
- table.add do |row|
15
+ table.add_row padding: [0, 2] do |row|
17
16
  row.add '[blue]Row 1 Col 0', align: :left, vertical: :middle
18
17
  row.add '[blue]Row 1 Col 1', "Line 2\nLine 3", align: :right
19
- row.add '[blue]Row 1 Col 2', align: :centered, vertical: :bottom
20
- row.padding = [0, 2]
18
+ row.add '[blue]Row 1 Col 2', align: :center, vertical: :bottom
21
19
  end
22
20
 
23
21
  filler_text = <<~FILLER
24
22
  This is some filler text to demonstrate word-wise line breaks inside
25
23
  a table cell. Please, just ignore this nonsense-text gently.
26
24
  FILLER
27
- table.add do |row|
28
- row.add '[blue]Row 2 Col 0', filler_text, eol: false, align: :right
29
- row.add '[blue]Row 2 Col 1', filler_text, eol: false, align: :centered
30
- row.add '[blue]Row 2 Col 2', filler_text, eol: false, align: :left
31
- row.padding = [1, 2]
32
- end
33
- end
34
25
 
35
- ui.space
36
- ui.table(border_style: :bright_blue, border: :default) do |table|
37
- table.add(*('A'..'Z').each_slice(2).map(&:join))
38
- table.add(*('😀'..'😌'))
39
- table.add(*(3..15).map { _1.to_s(3) })
40
- table.each { |row| row.assign(align: :centered, style: :bright_yellow) }
26
+ table.add_row padding: [1, 2], eol: false do |row|
27
+ row.add '[blue]Row 2 Col 0', filler_text, align: :right, max_width: 0.25
28
+ row.add '[blue]Row 2 Col 1', filler_text, align: :center
29
+ row.add '[blue]Row 2 Col 2', filler_text, align: :left, max_width: 0.25
30
+ end
41
31
  end
42
32
 
43
33
  ui.space
44
- ui.table do |table|
45
- table.add do |row|
46
- row.add 'green', style: :on_green, max_width: 0.1
47
- row.add 'blue', style: :on_blue, max_width: 0.25
48
- row.add 'red', style: :on_red, max_width: 0.1
49
- row.align = :centered
50
- end
51
- table.add do |row|
52
- row.add 'yellow', style: :on_yellow
53
- row.add 'magenta', style: :on_magenta
54
- row.add 'cyan', style: :on_cyan
55
- row.align = :centered
56
- end
34
+ ui.table border_style: :bright_blue, width: :max do |table|
35
+ table.add_row(*('A'..'Z').each_slice(2).map(&:join))
36
+ table.add_row(*('😀'..'😌'))
37
+ table.add_row(*(3..15).map { it.to_s(3) })
38
+ table.each_row { it.attributes.align = :center }
57
39
  end
58
40
  end
data/examples/tasks.rb CHANGED
@@ -2,51 +2,61 @@
2
2
 
3
3
  require_relative '../lib/natty-ui'
4
4
 
5
- ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Tasks[/]' do
5
+ ui.begin '[b]ᓚᕠᗢ NattyUI[/b] [i green]Tasks' do
6
6
  ui.space
7
7
  ui.puts <<~TEXT, eol: false
8
- Tasks are sections that are closed either successfully or with an error
9
- message. If successful, their content is only displayed temporarily and can
10
- consist of all other features, in particular further (sub)tasks. As an
11
- exception to this, some elements can be “pinned” as permanent content.
8
+ [i]Tasks[/i] are sections that are either completed successfully or result
9
+ in an error message. Upon successful completion, their content is displayed
10
+ only temporarily and may consist of any other elements, including additional
11
+ (sub)tasks. As an exception to this, some elements can be “pinned” as
12
+ permanent content.
12
13
  TEXT
13
14
 
14
15
  # to simulate some work:
15
- def do_something = sleep(0.25)
16
- def do_something_heavy = sleep(0.75)
16
+ simulate =
17
+ Module.new do
18
+ if ENV.key?('NO_SLEEP')
19
+ def self.work = nil
20
+ def self.heavy_work = nil
21
+ else
22
+ def self.work = sleep(0.2)
23
+ def self.heavy_work = sleep(1)
24
+ end
25
+ end
17
26
 
18
27
  ui.space
19
- ui.task 'Actualize Reading List' do
20
- ui.puts 'This is a simple which actualizes the book reading list.'
28
+ ui.task 'Update Reading List' do
29
+ ui.puts 'This is an example which actualizes the book reading list.'
21
30
 
22
31
  ui.task 'Connect to Library' do
23
- do_something
32
+ simulate.work
24
33
  ui.mark 'Server Found', mark: :checkmark
25
- ui.task('Login...') { do_something_heavy }
34
+ ui.task('Login...') { simulate.heavy_work }
26
35
  end
27
36
 
28
- ui.task('Request New Books') { do_something_heavy }
37
+ ui.task('Request New Books') { simulate.heavy_work }
29
38
 
30
- bar = ui.progress('Loading Books...', pin: true)
31
- 11.times do
32
- do_something
33
- bar.step
39
+ ui.progress('Loading Books...') do |progress|
40
+ while progress.value < 17
41
+ simulate.work
42
+ progress.step
43
+ end
34
44
  end
35
- bar.ok 'Books Loaded'
45
+ ui.ok '17 Books Loaded'
36
46
 
37
- ui.task('Disconnect from Library') { do_something_heavy }
47
+ ui.task('Disconnect from Library') { simulate.heavy_work }
38
48
 
39
- ui.progress 'Read Cover Images', max: 11 do |progress|
49
+ ui.progress 'Read Cover Images', max: 13 do |progress|
40
50
  while progress.value < progress.max
41
- do_something
51
+ simulate.work
42
52
  progress.step
43
53
  end
44
54
  end
45
55
 
46
56
  ui.pin 'New Books Marked', mark: :checkmark
47
57
 
48
- ui.task('Optimize Database') { do_something_heavy }
58
+ ui.task('Optimize Database') { simulate.heavy_work }
49
59
 
50
- ui.task('Remove Dust') { do_something_heavy }
60
+ ui.task('Remove Dust') { simulate.heavy_work }
51
61
  end
52
62
  end
data/examples/vbars.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative '../lib/natty-ui'
4
4
 
5
- ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Print Vertical Bars[/]' do
5
+ ui.begin '[b]ᓚᕠᗢ NattyUI[/b] [i green]Print Vertical Bars' do
6
6
  values = [
7
7
  11.97,
8
8
  14.35,
@@ -27,13 +27,15 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Print Vertical Bars[/]' do
27
27
  ].freeze
28
28
 
29
29
  ui.space
30
- ui.vbars values, style: :blue
30
+ ui.vbars values, style: :blue, bar_width: :auto
31
31
  ui.puts 'NattyUI can quick dump values as vertical bars.'
32
32
 
33
33
  ui.space
34
- ui.vbars values, style: :green, normalize: true, bar_width: 2
34
+ ui.vbars values, style: :green, normalize: true
35
35
  ui.puts <<~INFO, eol: false
36
36
  These are the same values but [i]normalized[/i]
37
37
  and printed with a fixed bar width.
38
38
  INFO
39
+
40
+ ui.space
39
41
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'element'
4
+
5
+ module NattyUI
6
+ # @private
7
+ class DumbProgress < Element
8
+ # @return [Numeric]
9
+ attr_reader :max
10
+
11
+ # @return [Numeric]
12
+ attr_reader :value
13
+
14
+ # @attribute [w] value
15
+ def value=(val)
16
+ return if @done != 0
17
+ return if @value == (val = @max ? val.clamp(0, @max) : [0, val].max)
18
+ @value = val
19
+ draw_bar if @max
20
+ end
21
+
22
+ def update(*title, value: nil, **)
23
+ return if @done != 0
24
+ draw_title(*title, **) unless title.empty?
25
+ if value
26
+ self.value = value
27
+ elsif @max
28
+ draw_bar
29
+ end
30
+ self
31
+ end
32
+
33
+ def step(*, count: 1, **) = update(*, value: value + count, **)
34
+
35
+ # @private
36
+ def puts(...) = @done == 0 ? super : self
37
+
38
+ private
39
+
40
+ def done
41
+ @done += 1
42
+ end
43
+
44
+ def draw_bar
45
+ diff = @value / @max.to_f
46
+ return if (fill = '█' * (20 * diff)).empty? || @last == fill
47
+ percent = diff == 1 ? '100.0%' : format('%.2f%%', 100 * diff)
48
+ percent = "0#{percent}" if percent.size < 6
49
+ @parent.puts(
50
+ "#{percent}▕#{@last = fill}#{'░' * (20 - fill.size)}▏",
51
+ prefix: @prefix
52
+ )
53
+ end
54
+
55
+ def draw_title(*, **options)
56
+ options[:cprefix] = Utils.mark[:active]
57
+ options[:prefix] = @prefix = Utils.mark[:none]
58
+ @parent.puts(*, **options)
59
+ end
60
+
61
+ def initialize(parent, max, *title, **)
62
+ super(parent)
63
+ @value = 0
64
+ @max = [0, max].max if max
65
+ draw_title(*title, **) unless title.empty?
66
+ end
67
+ end
68
+ end
@@ -3,37 +3,80 @@
3
3
  require_relative 'features'
4
4
 
5
5
  module NattyUI
6
- # Base element class supporting all {Features}.
6
+ # Base class for all UI container elements.
7
7
  #
8
+ # Instances are never created directly; they are returned (or yielded) by the
9
+ # section-opening methods on {Features}: {#section}, {#frame},
10
+ # {#task}, {#progress}, {#margin}, {#temporary}, and their typed variants.
11
+ #
12
+ # Every `Element` includes {Features}, so all output and section methods are
13
+ # available on the element itself.
14
+ #
15
+ # @example Using an element returned without a block
16
+ # frm = ui.frame 'Progress'
17
+ # frm.puts 'Loading…'
18
+ # frm.end # closes the frame
19
+ #
20
+ # @example The element is also yielded inside a block
21
+ # ui.section 'Summary' do |sec|
22
+ # sec.ok 'Done'
23
+ # end
8
24
  class Element
9
25
  include Features
10
26
 
11
- # Close the element.
12
27
  #
13
- # @return [Features] current element
28
+ # @!group Section elements
29
+ #
30
+
31
+ # Closes this element and returns the parent element.
32
+ #
33
+ # Calling `end` on an already-closed element is a no-op. All output
34
+ # methods on this element become no-ops after `end` is called.
35
+ #
36
+ # @example
37
+ # frm = ui.frame 'Results'
38
+ # ui.puts 'Content'
39
+ # frm.end # closes the frame, returns NattyUI (or the parent element)
40
+ #
41
+ # @return [Features] the parent element (or {NattyUI} if this was the
42
+ # outermost element)
14
43
  def end = NattyUI.__send__(:_end, self)
15
44
 
45
+ #
46
+ # @!endgroup
47
+ #
48
+
16
49
  # @private
17
- def columns = @parent.columns - @prefix_width - @suffix_width
50
+ def columns
51
+ ret = @parent.columns
52
+ ret -= @prefix.width if @prefix
53
+ @suffix ? ret - @suffix.width : ret
54
+ end
18
55
 
19
56
  # @private
20
- def puts(*objects, **options)
57
+ def puts(*, **options)
21
58
  if @prefix
22
- options[:prefix] = "#{@prefix}#{options[:prefix]}"
23
- options[:prefix_width] = options[:prefix_width].to_i + @prefix_width
24
- if (first_line = options[:first_line_prefix])
25
- options[:first_line_prefix] = "#{@prefix}#{first_line}"
26
- if (flw = options[:first_line_prefix_width])
27
- options[:first_line_prefix_width] = flw + @prefix_width
28
- end
59
+ pref = options[:prefix]
60
+ if (cpref = options[:cprefix]).nil? || cpref == true
61
+ options[:prefix] = pref ? @prefix + pref : @prefix
62
+ else
63
+ cpref = StrConst[cpref] unless StrConst === cpref
64
+ options[:cprefix] = @prefix + cpref
65
+ options[:prefix] = @prefix + (pref || StrConst.spacer(cpref.width))
29
66
  end
30
67
  end
31
68
  if @suffix
32
- options[:suffix] = "#{options[:suffix]}#{@suffix}"
33
- options[:suffix_width] = options[:suffix_width].to_i + @suffix_width
69
+ suff = options[:suffix]
70
+ if (csuff = options[:csuffix]).nil? || csuff == true
71
+ options[:suffix] = suff ? suff + @suffix : @suffix
72
+ else
73
+ csuff = StrConst[csuff] unless StrConst === csuff
74
+ options[:csuffix] = csuff + @suffix
75
+ options[:suffix] = (suff || StrConst.spacer(csuff.width)) + @suffix
76
+ end
34
77
  end
35
78
  # options[:max_width] = @max_width if @max_width
36
- @parent.puts(*objects, **options)
79
+ @parent.puts(*, **options)
37
80
  self
38
81
  end
39
82
 
@@ -45,63 +88,11 @@ module NattyUI
45
88
 
46
89
  private
47
90
 
91
+ def done = raise(NotImplementedError)
92
+
48
93
  def initialize(parent)
49
94
  @parent = parent
50
- @prefix_width = @suffix_width = 0
51
- end
52
- end
53
-
54
- # Methods for elements with status like {ProgressHelper}, {Section}, {Task}.
55
- #
56
- module WithStatus
57
- # @return [Symbol, nil] element's current status
58
- attr_reader :status
59
-
60
- # @attribute [r] active?
61
- # @return [true, false] whether the element is still active
62
- def active? = @status.nil?
63
-
64
- # @attribute [r] closed?
65
- # @return [true, false] whether the element is closed
66
- def closed? = !active?
67
-
68
- # @attribute [r] closed?
69
- # @return [true, false] whether the element is completed
70
- def ok? = @state == :ok
71
-
72
- # @attribute [r] failed?
73
- # @return [true, false] whether the element is completed with failure
74
- def failed? = @state == :failed
75
-
76
- # Close the element.
77
- #
78
- # @param [#to_s] text optional message
79
- # @return [Features] current element
80
- def ok(*text)
81
- return self if @state
82
- text = [@title] if text.empty? && @title
83
- _done(text)
84
- @state = :ok
85
- self.end
86
- end
87
- alias done ok
88
-
89
- # Close the element with a failure.
90
- #
91
- # @param text [#to_s] optional message
92
- # @yield optionally, when block is given
93
- # @yieldparam failed [Section] see {Features.failed}
94
- # @return [Features] current element
95
- def failed(*text, &block)
96
- return self if @state
97
- if text.empty?
98
- super(@title, &block).ok if @title
99
- else
100
- super.ok
101
- end
102
- @state = :failed
103
- _failed
104
- self.end
95
+ @done = 0
105
96
  end
106
97
  end
107
98
  end