natty-ui 0.25.0 → 0.27.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23eed1ba95aaecb2555dfeed5a6fedbcd827299d1e4a64c91952953b3d36d787
4
- data.tar.gz: 53380e5ebe647a96abbc24baecd1f24a7a3290544ac10e34e3aa8b1e8dba1e17
3
+ metadata.gz: 43f1afe6be5864d4895c974783fbfcff1f4b5a23f963eb24f6a964fe09b1174c
4
+ data.tar.gz: e3a4c8e1981f0299f9927c4a3f53fa0c4eca95f792f9590c9ea191e7018adcb5
5
5
  SHA512:
6
- metadata.gz: 56c829ee249c7cecce24bb9b88c131e8fdaaec20aff2f0340b16d701c77907805f7595d38cfe0e122d4f7365d6ae6babfccdd680b9754de283c7e63c944e78ea
7
- data.tar.gz: 9964afa255fe3a76d59727f13edcafa31429768729e3b14408f6142f6af17236c7831836857897a0e860044732ad5a347c6bc4efb06ef277412d73d67167519d
6
+ metadata.gz: d9c75d24c0363076c3e0d41ac458d4f13a6552811d166028d5d758ba408d20aa63907a471ee56c4fe4b0638db1cb03e915d0b6c606e8770a9753d59eafb7c951
7
+ data.tar.gz: 77a95400ba528d57f3e1b559ac1ffa584c68a60bcc45d212341bc803031f204abac44e269db1d81903a4629b7c9c466c27acae66d84c2f802a3486bab37a929b
@@ -3,12 +3,12 @@
3
3
  require_relative '../lib/natty-ui'
4
4
 
5
5
  ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]24-bit Color Support[/]' do
6
- ui.space
7
6
  bar = '█' * ui.columns
8
7
  rainbow =
9
- (7..17).map do |i|
10
- NattyUI::Ansi.rainbow(bar, seed: 3, spread: 3.25, frequency: i / 100.0)
8
+ (7..21).map do |i|
9
+ NattyUI::Ansi.rainbow(bar, seed: 3, spread: 3.25, frequency: i / 200.0)
11
10
  end
12
11
 
12
+ ui.space
13
13
  ui.puts(*rainbow)
14
14
  end
@@ -4,7 +4,7 @@ require_relative '../lib/natty-ui'
4
4
 
5
5
  ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]3/4bit Color Support[/]' do
6
6
  ui.space
7
- ui.puts <<~INFO, ignore_newline: true
7
+ ui.puts <<~INFO, eol: false
8
8
  Terminals may support colors. You can colorize foreground text and
9
9
  background. The very basic color palette consists of eight colors and may
10
10
  be extended with eight colors which are much brighter.
@@ -4,7 +4,7 @@ require_relative '../lib/natty-ui'
4
4
 
5
5
  ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]ANSI Attributes[/]' do
6
6
  ui.space
7
- ui.puts <<~INFO_1, <<~INFO_2, ignore_newline: true
7
+ ui.puts <<~INFO_1, <<~INFO_2, eol: false
8
8
  NattyUI supports all well known attributes like
9
9
  [b]bold[/b],
10
10
  [i]italic[/i],
data/examples/examples.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative '../lib/natty-ui'
4
4
 
5
5
  EXAMPLES = {
6
+ 'info' => 'Terminal Information',
6
7
  'attributes' => 'ANSI Attributes',
7
8
  '3bit-colors' => '3/4bit Color Support',
8
9
  '8bit-colors' => '8bit Color Support',
data/examples/info.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/natty-ui'
4
+
5
+ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Terminal Information[/]' do
6
+ ui.space
7
+ ui.table do |table|
8
+ table.add('Identifier', Terminal.application || 'unidentified')
9
+ table.add('ANSI support', Terminal.ansi? ? 'yes' : 'no')
10
+ table.add('Input mode', Terminal.input_mode)
11
+ table.add(
12
+ 'Colors',
13
+ "#{Terminal.true_color? ? 'true color' : Terminal.colors}
14
+ [black]██[red]██[green]██[yellow]██[blue]██[magenta]██[cyan]██[white]██" \
15
+ '[bright_black]██[bright_red]██[bright_green]██[bright_yellow]██' \
16
+ '[bright_blue]██[bright_magenta]██[bright_cyan]██[bright_white]██[/]' \
17
+ "#{
18
+ if Terminal.true_color?
19
+ Terminal::Ansi.rainbow("\n████████████████████████████████")
20
+ end
21
+ }"
22
+ )
23
+ table.add('Screen size', Terminal.size.join(' x '))
24
+ fc = table.columns[0]
25
+ fc.width = 13
26
+ fc.padding_right = 1
27
+ fc.align = :right
28
+ table.columns[1].style = :bold
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/natty-ui'
4
+
5
+ raw = name = :start
6
+ while name != 'Esc'
7
+ ui.temporary do
8
+ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Keyboard Key Codes[/]' do
9
+ ui.puts(
10
+ "\n#{
11
+ case name
12
+ when :start
13
+ "Your terminal uses [i]#{
14
+ Terminal.input_mode
15
+ }[/i] mode. Press a key!"
16
+ when nil
17
+ "[green]#{raw.inspect}[/fg] → [bold bright_green][\\#{raw}]"
18
+ else
19
+ "[green]#{raw.inspect}[/fg] → [bold bright_green]#{
20
+ name.split('+').map! { "[\\#{_1}]" }.join(' ')
21
+ }"
22
+ end
23
+ }\n\n[bright_black](Exit with ESC)"
24
+ )
25
+ end
26
+ raw, name = Terminal.read_key(mode: :both)
27
+ end
28
+ end
data/examples/ls.rb CHANGED
@@ -31,4 +31,20 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Print Lists[/]' do
31
31
  ui.space
32
32
  ui.h2 'Compact Display'
33
33
  ui.ls lorem, glyph: 1
34
+
35
+ ui.space
36
+ ui.h2 'Latin Char Glyph'
37
+ ui.ls lorem.take(5), glyph: :a
38
+
39
+ ui.space
40
+ ui.h2 'Hex Numbers'
41
+ ui.ls lorem.take(5), glyph: '0x08'
42
+
43
+ ui.space
44
+ ui.h2 'Chapter Numbering'
45
+ ui.ls lorem.take(5), glyph: :'1.8'
46
+
47
+ ui.space
48
+ ui.h2 'Custom Glyph'
49
+ ui.ls lorem.take(5), glyph: '[b bright_yellow]→[/]'
34
50
  end
@@ -18,6 +18,6 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Named Colors Support[/]' do
18
18
  NattyUI::Ansi
19
19
  .named_colors
20
20
  .delete_if { /\d/.match?(_1) }
21
- .map! { "[bg_#{_1}] [/bg] [#{_1}]#{_1.to_s.ljust(22)}[/fg]" }
21
+ .map! { "[bg_#{_1}] [/bg] [#{_1}]#{_1}[/fg]" }
22
22
  )
23
23
  end
data/examples/sections.rb CHANGED
@@ -5,7 +5,7 @@ require_relative '../lib/natty-ui'
5
5
  ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Sections[/]' do
6
6
  ui.space
7
7
  ui.information 'Informative Message' do
8
- ui.puts <<~TEXT, ignore_newline: true
8
+ ui.puts <<~TEXT, eol: false
9
9
  Sections and messages are elements which support any other feature. This
10
10
  means they may contain text, other sections, titles, horizontal rules,
11
11
  lists, progress bars and much more!
@@ -13,7 +13,7 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Sections[/]' do
13
13
  end
14
14
 
15
15
  ui.space
16
- ui.warning 'Warning', 'This is a warning message example.'
16
+ ui.warning "Warning\nThis is a warning message example."
17
17
 
18
18
  ui.space
19
19
  ui.error 'Error Message' do
@@ -23,7 +23,4 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Sections[/]' do
23
23
  cc.add 'You can add all other elements to a section.', vertical: :middle
24
24
  end
25
25
  end
26
-
27
- ui.space
28
- ui.failed "Fail Message\nA sad failure message."
29
26
  end
data/examples/tables.rb CHANGED
@@ -46,10 +46,9 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Print Tables[/]' do
46
46
  ui.space
47
47
  ui.table do |table|
48
48
  table.add do |row|
49
- row.add 'green', style: :on_green
50
- row.add 'blue', style: :on_blue
51
- row.add 'red', style: :on_red
52
- row.width = 15
49
+ row.add 'green', style: :on_green, max_width: 0.1
50
+ row.add 'blue', style: :on_blue, max_width: 0.25
51
+ row.add 'red', style: :on_red, max_width: 0.1
53
52
  row.align = :centered
54
53
  end
55
54
  table.add do |row|
data/examples/tasks.rb CHANGED
@@ -4,7 +4,7 @@ require_relative '../lib/natty-ui'
4
4
 
5
5
  ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Tasks[/]' do
6
6
  ui.space
7
- ui.puts <<~TEXT, ignore_newline: true
7
+ ui.puts <<~TEXT, eol: false
8
8
  Tasks are sections that are closed either successfully or with an error message.
9
9
  If successful, their content is only displayed temporarily and can consist of all
10
10
  other features, in particular further (sub)tasks. As an exception to this, some
@@ -11,19 +11,13 @@ module NattyUI
11
11
  end
12
12
 
13
13
  # @return [Attributes] itself
14
- def merge!(**attributes)
15
- attributes.empty? ? self : _assign(attributes)
16
- end
14
+ def merge!(**attributes) = attributes.empty? ? self : _assign(attributes)
17
15
 
18
16
  # @!visibility private
19
- def to_hash
20
- _store({})
21
- end
17
+ def to_hash = _store({})
22
18
 
23
19
  # @!visibility private
24
- def to_h(&block)
25
- block ? _store({}).to_h(&block) : _store({})
26
- end
20
+ def to_h(&block) = block ? _store({}).to_h(&block) : _store({})
27
21
 
28
22
  private
29
23
 
@@ -41,17 +35,22 @@ module NattyUI
41
35
 
42
36
  def as_wh(value)
43
37
  return unless value
44
- return (value = value.to_i) > 0 ? value : nil if value.is_a?(Numeric)
45
- value.is_a?(Range) ? wh_from(value.begin.to_i, value.end.to_i) : nil
38
+ return value > 0 ? value : nil if value.is_a?(Numeric)
39
+ value.is_a?(Range) ? wh_from(value.begin, value.end) : nil
46
40
  end
47
41
 
48
42
  def wh_from(min, max)
49
- min = nil if min < 1
50
- max = nil if max < 1
43
+ min = normalized(min)
44
+ max = normalized(max)
51
45
  return max ? (..max) : nil unless min
52
- return(min..) unless max
46
+ return Range.new(min, nil) unless max
53
47
  min == max ? min : Range.new(*[min, max].minmax)
54
48
  end
49
+
50
+ def normalized(value)
51
+ return value < 0 ? nil : value if value.is_a?(Float) && value < 1
52
+ (value = value.to_i) < 1 ? nil : value
53
+ end
55
54
  end
56
55
 
57
56
  module Align
@@ -83,6 +82,30 @@ module NattyUI
83
82
  end
84
83
  end
85
84
 
85
+ module Position
86
+ # Horizontal element position.
87
+ #
88
+ # @return [nil, :right, :centered]
89
+ attr_reader :position
90
+
91
+ # @attribute [w] position
92
+ def position=(value)
93
+ @position = Utils.position(value)
94
+ end
95
+
96
+ protected
97
+
98
+ def _assign(opt)
99
+ self.position = opt[:position] if opt.key?(:position)
100
+ super
101
+ end
102
+
103
+ def _store(opt)
104
+ opt[:position] = @position if @position
105
+ super
106
+ end
107
+ end
108
+
86
109
  module Vertical
87
110
  # Vertical element alignment.
88
111
  #
@@ -129,26 +152,22 @@ module NattyUI
129
152
  #
130
153
  # @attribute [r] min_width
131
154
  # @return [Integer, nil]
132
- def min_width
133
- width.is_a?(Range) ? @width.begin : @width
134
- end
155
+ def min_width = width.is_a?(Range) ? @width.begin : @width
135
156
 
136
157
  # @attribute [w] min_width
137
158
  def min_width=(value)
138
- @width = wh_from(value.to_i, max_width.to_i)
159
+ @width = wh_from(value, max_width)
139
160
  end
140
161
 
141
162
  # Maximum element width.
142
163
  #
143
164
  # @attribute [r] max_width
144
165
  # @return [Integer, nil]
145
- def max_width
146
- width.is_a?(Range) ? @width.end : @width
147
- end
166
+ def max_width = width.is_a?(Range) ? @width.end : @width
148
167
 
149
168
  # @attribute [w] max_width
150
169
  def max_width=(value)
151
- @width = wh_from(min_width.to_i, value.to_i)
170
+ @width = wh_from(min_width, value)
152
171
  end
153
172
 
154
173
  protected
@@ -183,9 +202,7 @@ module NattyUI
183
202
  #
184
203
  # @attribute [r] min_height
185
204
  # @return [Integer, nil]
186
- def min_height
187
- @height.is_a?(Range) ? @height.begin : @height
188
- end
205
+ def min_height = @height.is_a?(Range) ? @height.begin : @height
189
206
 
190
207
  # @attribute [w] min_height
191
208
  def min_height=(value)
@@ -196,9 +213,7 @@ module NattyUI
196
213
  #
197
214
  # @attribute [r] max_height
198
215
  # @return [Integer, nil]
199
- def max_height
200
- @height.is_a?(Range) ? @height.begin : @height
201
- end
216
+ def max_height = @height.is_a?(Range) ? @height.begin : @height
202
217
 
203
218
  # @attribute [w] max_height
204
219
  def max_height=(value)
@@ -464,30 +479,6 @@ module NattyUI
464
479
  end
465
480
  end
466
481
 
467
- module BorderAround
468
- # Whether the border is around an element.
469
- #
470
- # @return [true, false]
471
- attr_reader :border_around
472
-
473
- # @attribute [w] border_around
474
- def border_around=(value)
475
- @border_around = value ? true : false
476
- end
477
-
478
- protected
479
-
480
- def _assign(opt)
481
- @border_around = opt[:border_around]
482
- super
483
- end
484
-
485
- def _store(opt)
486
- opt[:border_around] = true if @border_around
487
- super
488
- end
489
- end
490
-
491
482
  module Border
492
483
  # Border type.
493
484
  #
@@ -568,10 +559,8 @@ module NattyUI
568
559
  attr_reader :text
569
560
 
570
561
  def empty? = @text.empty?
571
-
572
562
  alias _to_s to_s
573
563
  private :_to_s
574
-
575
564
  def to_str = @text.join("\n")
576
565
  alias to_s to_str
577
566
 
@@ -33,10 +33,10 @@ module NattyUI
33
33
  end
34
34
 
35
35
  alias _to_s to_s
36
+ private :_to_s
36
37
 
37
38
  # @!visibility private
38
39
  alias to_s inspect
39
- private :_to_s
40
40
 
41
41
  private
42
42
 
@@ -46,33 +46,30 @@ module NattyUI
46
46
  end
47
47
  end
48
48
 
49
- module StateMixin
50
- attr_reader :state
49
+ module WithStatus
50
+ attr_reader :status
51
51
 
52
- def closed? = @state ? true : false
52
+ def active? = @status.nil?
53
+ def closed? = !active?
53
54
  def ok? = @state == :ok
54
55
  def failed? = @state == :failed
55
56
 
56
- # @return [Element] itself
57
- def done(*text) = @state ? self : finish_ok(text)
58
- alias ok done
59
-
60
- # @!visibility private
61
- def failed(title, *msg)
62
- return if @state
63
- super
64
- finish_failed
57
+ def ok(*text)
58
+ return self if @state
59
+ text = [@title] if text.empty? && @title
60
+ _done(text)
61
+ @state = :ok
62
+ self
65
63
  end
64
+ alias done ok
66
65
 
67
- # @!visibility private
68
- def inspect = "#{_to_s.chop} state=#{@state.inspect}>"
69
-
70
- protected
71
-
72
- def finish_failed
66
+ def failed(*text, &block)
67
+ return self if @state
68
+ _failed
73
69
  @state = :failed
70
+ text = [@title] if text.empty? && @title
71
+ @parent.failed(*text, &block)
74
72
  self
75
73
  end
76
74
  end
77
- private_constant :StateMixin
78
75
  end
@@ -31,8 +31,8 @@ module NattyUI
31
31
  # @param options [{Symbol => Object}]
32
32
  # @option options [:left, :right, :centered] :align (:left)
33
33
  # text alignment
34
- # @option options [true, false] :ignore_newline (false)
35
- # whether to igniore newline characters
34
+ # @option options [true, false] :eol (true)
35
+ # whether to respect newline characters
36
36
  #
37
37
  # @return [Features]
38
38
  # itself
@@ -84,7 +84,7 @@ module NattyUI
84
84
  limit: max_width,
85
85
  bbcode: bbcode,
86
86
  ansi: Terminal.ansi?,
87
- ignore_newline: options[:ignore_newline]
87
+ ignore_newline: options[:eol] == false || options[:ignore_newline]
88
88
  )
89
89
 
90
90
  if (align = options[:align]).nil?
@@ -292,7 +292,7 @@ module NattyUI
292
292
  #
293
293
  # @return (see puts)
294
294
  def space(count = 1)
295
- (count = count.to_i).positive? ? puts(*Array.new(count, "\n")) : self
295
+ puts("\n" * count) if (count = count.to_i).positive?
296
296
  end
297
297
 
298
298
  # Print given items as list (like 'ls' command).
@@ -327,7 +327,7 @@ module NattyUI
327
327
  # Generate and print a table.
328
328
  # See {Table} for much more details about table generation.
329
329
  #
330
- # @example Draw a very simple table with three rows, four columns, complete borders
330
+ # @example Draw a very simple 3x4 table with complete borders
331
331
  # ui.table(border: :default, border_around: true, padding: [0, 1]) do |table|
332
332
  # table.add 1, 2, 3, 4
333
333
  # table.add 5, 6, 7, 8
@@ -338,13 +338,16 @@ module NattyUI
338
338
  # attributes for the table and default attributes for table cells
339
339
  # @option attributes [Symbol] :border (nil)
340
340
  # kind of border,
341
- # see {Attributes::Border}
342
- # @option attributes [true, false] :border_around (false)
343
- # whether the table should have a border around,
344
- # see {Attributes::BorderAround}
341
+ # see {Table::Attributes}
345
342
  # @option attributes [Enumerable<Symbol>] :border_style (nil)
346
343
  # style of border,
347
- # see {Attributes::BorderStyle}
344
+ # see {Table::Attributes}
345
+ # @option attributes [true, false] :border_around (false)
346
+ # whether the table should have a border around,
347
+ # see {Table::Attributes}
348
+ # @option attributes [:left, :right, :centered] :position (false)
349
+ # where to align the table,
350
+ # see {Table::Attributes}
348
351
  #
349
352
  # @yieldparam table [Table]
350
353
  # helper to define the table layout
@@ -353,7 +356,11 @@ module NattyUI
353
356
  def table(**attributes)
354
357
  return self unless block_given?
355
358
  yield(table = Table.new(**attributes))
356
- puts(*TableRenderer[table, columns])
359
+ puts(
360
+ *TableRenderer[table, columns],
361
+ align: table.attributes.position,
362
+ expand: true
363
+ )
357
364
  end
358
365
 
359
366
  # Print text in columns.
@@ -372,9 +379,10 @@ module NattyUI
372
379
  #
373
380
  # @return (see puts)
374
381
  def cols(*columns, **attributes)
375
- table(**attributes) do |table|
382
+ tab_att, att = Utils.split_table_attr(attributes)
383
+ table(**tab_att) do |table|
376
384
  table.add do |row|
377
- columns.each { row.add(_1, **attributes) }
385
+ columns.each { row.add(_1, **att) }
378
386
  yield(row) if block_given?
379
387
  end
380
388
  end
@@ -403,9 +411,9 @@ module NattyUI
403
411
  # @return (see puts)
404
412
  def div(*text, **attributes)
405
413
  return self if text.empty?
406
- table(border_around: true, **attributes) do |table|
407
- table.add { _1.add(*text, **attributes) }
408
- end
414
+ tab_att, att = Utils.split_table_attr(attributes)
415
+ tab_att[:border_around] = true
416
+ table(**tab_att) { |table| table.add { _1.add(*text, **att) } }
409
417
  end
410
418
 
411
419
  # Dynamically display a task progress.
@@ -444,7 +452,7 @@ module NattyUI
444
452
  # @param pin [true, false]
445
453
  # whether the final progress state should be "pinned" to parent element
446
454
  #
447
- # @return [Progress]
455
+ # @return [ProgressHelper]
448
456
  # itself
449
457
  #
450
458
  # @overload progress(title, max: nil, pin: false, &block)
@@ -455,7 +463,7 @@ module NattyUI
455
463
  # @param pin [true, false]
456
464
  # whether the final progress state should be "pinned" to parent element
457
465
  #
458
- # @yieldparam progress [Progress]
466
+ # @yieldparam progress [ProgressHelper]
459
467
  # itself
460
468
  #
461
469
  # @return [Object]
@@ -555,7 +563,7 @@ module NattyUI
555
563
  # @param align [:left, :right, :centered]
556
564
  # text alignment,
557
565
  # see {Attributes::Align}
558
- # @param border: [Symbol]
566
+ # @param border [Symbol]
559
567
  # kind of border,
560
568
  # see {Attributes::Border}
561
569
  # @param border_style [Enumerable<Symbol>]
@@ -19,7 +19,7 @@ module NattyUI
19
19
  def as_items(items, glyph)
20
20
  items.flatten!
21
21
  glyph = as_glyph(glyph, items.size)
22
- items.map! { |item| Item.new(item = glyph[item], Text.width(item)) }
22
+ items.map! { Item.new(glyph[_1]) }
23
23
  end
24
24
 
25
25
  def as_glyph(glyph, size)
@@ -27,30 +27,34 @@ module NattyUI
27
27
  when nil, false
28
28
  lambda(&:itself)
29
29
  when :hex
30
- pad = size.to_s(16).size
30
+ pad = [2, size.to_s(16).size].max
31
31
  glyph = 0
32
32
  ->(s) { "#{(glyph += 1).to_s(16).rjust(pad, '0')} #{s}" }
33
+ when /\A0x(\h+)\z/
34
+ glyph = Regexp.last_match(1).hex - 1
35
+ pad = [2, (glyph + size).to_s(16).size].max
36
+ ->(s) { "0x#{(glyph += 1).to_s(16).rjust(pad, '0')} #{s}" }
33
37
  when Integer
34
38
  pad = (glyph + size).to_s.size
35
39
  glyph -= 1
36
- ->(s) { "#{(glyph += 1).to_s.rjust(pad)} #{s}" }
40
+ ->(s) { "#{(glyph += 1).to_s.rjust(pad, ' ')} #{s}" }
37
41
  when Symbol
38
- lambda do |s|
39
- "#{
40
- t = glyph
41
- glyph = glyph.succ
42
- t
43
- } #{s}[/]"
44
- end
42
+ ->(s) { "#{[glyph, glyph = glyph.succ][0]} #{s}" }
45
43
  else
46
44
  ->(s) { "#{glyph} #{s}" }
47
45
  end
48
46
  end
49
47
 
50
- Item =
51
- Struct.new(:str, :width) do
52
- def to_s(in_width) = "#{str}#{' ' * (in_width - width)}"
48
+ class Item
49
+ attr_reader :width
50
+
51
+ def to_s(in_width) = "#{@str}#{' ' * (in_width - @width)}"
52
+
53
+ def initialize(str)
54
+ @str = str
55
+ @width = Text.width(str)
53
56
  end
57
+ end
54
58
  private_constant :Item
55
59
  end
56
60
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'element'
4
+
3
5
  module NattyUI
4
6
  # @todo This chapter needs more documentation.
5
7
  #
6
8
  # Progress indictaor helper used by {Features.progress}.
7
9
  #
8
10
  module ProgressHelper
11
+ include WithStatus
12
+
9
13
  # @return [String]
10
14
  attr_reader :title
11
15
 
@@ -40,21 +44,12 @@ module NattyUI
40
44
  redraw
41
45
  end
42
46
 
43
- def active? = @status.nil?
44
- def ok? = @status == :ok
45
- def failed? = @status == :failed
46
-
47
47
  def step(count: 1, title: nil)
48
48
  @title = title if title
49
49
  self.value += count
50
50
  self
51
51
  end
52
52
 
53
- def done(title = nil) = finish(:ok, title)
54
- alias ok done
55
-
56
- def failed(title = nil) = finish(:failed, title)
57
-
58
53
  alias _to_s to_s
59
54
  private :_to_s
60
55
 
@@ -80,31 +75,33 @@ module NattyUI
80
75
 
81
76
  private
82
77
 
83
- def finish(status, title)
84
- return if @status
85
- @status = status
78
+ def _done(text)
86
79
  NattyUI.back_to_line(@pin_line)
87
- if status == :failed
88
- @parent.failed(title || @title)
89
- else
90
- cm = Theme.current.mark(:checkmark)
91
- @parent.puts(
92
- title || @title,
93
- pin: @pin,
94
- first_line_prefix: cm,
95
- first_line_prefix_width: cm.width
96
- )
97
- end
98
- self
80
+ @pin_line = nil
81
+ cm = Theme.current.mark(:checkmark)
82
+ @parent.puts(
83
+ *text,
84
+ pin: @pin,
85
+ first_line_prefix: cm,
86
+ first_line_prefix_width: cm.width
87
+ )
88
+ end
89
+
90
+ def _failed
91
+ NattyUI.back_to_line(NattyUI.lines_written - 1) if @last&.size == 2
92
+ @pin_line = nil
99
93
  end
100
94
 
101
95
  def initialize(parent, title, max, pin)
102
96
  @parent = parent
103
- @value = 0.0
97
+ @value = 0
104
98
  @title = title
105
99
  @pin = pin
106
100
  @pin_line = NattyUI.lines_written
107
101
  @style = Theme.current.task_style
102
+ cm = Theme.current.mark(:current)
103
+ @flp = "#{cm} #{@style}"
104
+ @flpw = cm.width + 1
108
105
  max ? self.max = max : redraw
109
106
  end
110
107
 
@@ -116,14 +113,14 @@ module NattyUI
116
113
  @pin_line = NattyUI.back_to_line(@pin_line) if @last
117
114
  @parent.puts(
118
115
  *curr,
119
- first_line_prefix: "#{@style}➔ ",
120
- first_line_prefix_width: 2
116
+ first_line_prefix: @flp,
117
+ first_line_prefix_width: @flpw
121
118
  )
122
119
  @last = curr
123
120
  end
124
121
 
125
122
  def moving_bar
126
- "#{@style}#{'·' * @value}" if @value >= 1
123
+ "#{@style}#{'' * @value}" if @value >= 1
127
124
  end
128
125
 
129
126
  def bar(diff)
@@ -143,20 +140,18 @@ module NattyUI
143
140
 
144
141
  private
145
142
 
146
- def finish(status, title)
147
- return if @status
148
- @status = status
149
- if status == :failed
150
- @parent.failed(title || @title)
151
- else
152
- cm = Theme.current.mark(:checkmark)
153
- @parent.puts(
154
- title || @title,
155
- first_line_prefix: cm,
156
- first_line_prefix_width: cm.width
157
- )
158
- end
159
- self
143
+ def _done(text)
144
+ cm = Theme.current.mark(:checkmark)
145
+ @parent.puts(
146
+ *text,
147
+ pin: @pin,
148
+ first_line_prefix: cm,
149
+ first_line_prefix_width: cm.width
150
+ )
151
+ end
152
+
153
+ def _failed
154
+ # nop
160
155
  end
161
156
 
162
157
  def initialize(parent, title, max)
@@ -3,7 +3,7 @@
3
3
  require_relative 'element'
4
4
 
5
5
  module NattyUI
6
- # {Element} implemting a display section used by
6
+ # Display section used by
7
7
  #
8
8
  # - {Features.section}
9
9
  # - {Features.message}
@@ -13,23 +13,20 @@ module NattyUI
13
13
  # - {Features.failed}
14
14
  #
15
15
  class Section < Element
16
- include StateMixin
16
+ include WithStatus
17
17
 
18
18
  # @!visibility private
19
19
  def puts(*objects, **options) = @state ? self : super
20
20
 
21
21
  private
22
22
 
23
- def finish_ok(text)
23
+ def _done(text)
24
24
  puts(*text) unless text.empty?
25
- @state = :ok
26
25
  @parent.puts(@border.bottom)
27
- self
28
26
  end
29
27
 
30
- def finish_failed
28
+ def _failed
31
29
  @parent.puts(@border.bottom)
32
- super
33
30
  end
34
31
 
35
32
  def show_title(title)
@@ -51,7 +48,7 @@ module NattyUI
51
48
  @border = Theme.current.section_border(kind)
52
49
  show_title(title)
53
50
  @prefix = @border.prefix
54
- @prefix_width = @prefix.size
51
+ @prefix_width = @prefix.width
55
52
  puts(*rest) if rest && !rest.empty?
56
53
  puts(*msg) unless msg.empty?
57
54
  end
@@ -33,7 +33,7 @@ module NattyUI
33
33
  m = cell.attributes.max_width
34
34
  max = m if m && (max.nil? || max < m)
35
35
  end
36
- wh_from(min.to_i, max.to_i)
36
+ wh_from(min, max)
37
37
  end
38
38
 
39
39
  def respond_to_missing?(name, _)
@@ -181,7 +181,46 @@ module NattyUI
181
181
  class Attributes < NattyUI::Attributes::Base
182
182
  prepend NattyUI::Attributes::Border
183
183
  prepend NattyUI::Attributes::BorderStyle
184
- prepend NattyUI::Attributes::BorderAround
184
+ prepend NattyUI::Attributes::Position
185
+
186
+ # Whether the table has a border around.
187
+ #
188
+ # @return [true, false]
189
+ attr_reader :border_around
190
+
191
+ # @attribute [w] border_around
192
+ def border_around=(value)
193
+ @border_around = value ? true : false
194
+ end
195
+
196
+ # Maximum table width.
197
+ #
198
+ # @return [Integer, nil]
199
+ attr_reader :max_width
200
+
201
+ # @attribute [w] max_width
202
+ def max_width=(value)
203
+ if value.is_a?(Float)
204
+ return @max_width = nil if value < 0
205
+ return @max_width = value if value < 1
206
+ end
207
+ value = value.to_i
208
+ @max_width = value <= 0 ? nil : value
209
+ end
210
+
211
+ protected
212
+
213
+ def _assign(opt)
214
+ @border_around = opt[:border_around]
215
+ self.max_width = opt[:max_width]
216
+ super
217
+ end
218
+
219
+ def _store(opt)
220
+ opt[:border_around] = true if @border_around
221
+ opt[:max_width] = @max_width if @max_width
222
+ super
223
+ end
185
224
  end
186
225
 
187
226
  include Enumerable
@@ -207,10 +246,20 @@ module NattyUI
207
246
 
208
247
  # @return [Row] created row
209
248
  def add(*text, **attributes)
210
- nr = Row.new
211
- @rows << nr
212
- text.each { nr.add(_1, **attributes) }
213
- block_given? ? yield(nr) : nr
249
+ unless text[0].is_a?(Hash)
250
+ @rows << (nr = Row.new)
251
+ text.each { nr.add(_1, **attributes) }
252
+ return block_given? ? yield(nr) : nr
253
+ end
254
+ new_rows = []
255
+ text[0].each_pair do |key, value|
256
+ new_rows << (nr = Row.new)
257
+ @rows << nr
258
+ nr.add(key, **attributes)
259
+ nr.add(value, **attributes)
260
+ yield(nr) if block_given?
261
+ end
262
+ new_rows
214
263
  end
215
264
 
216
265
  def delete(row)
@@ -8,29 +8,42 @@ module NattyUI
8
8
  def self.[](table, max_width)
9
9
  columns = table.columns.map(&:width)
10
10
  return [] if columns.empty?
11
- attributes = table.attributes
12
- unless attributes.border_chars.nil?
11
+ att = table.attributes
12
+ case att.max_width
13
+ when Float
14
+ max_width *= att.max_width
15
+ when Integer
16
+ max_width = [max_width, att.max_width].min
17
+ end
18
+ unless att.border_chars.nil?
13
19
  max_width -= (columns.size - 1)
14
- max_width -= 2 if attributes.border_around
20
+ max_width -= 2 if att.border_around
15
21
  end
16
22
  return [] if max_width < columns.size
17
- new(columns, table.each.to_a, attributes, max_width).lines
23
+ new(columns, table.each.to_a, att, max_width).lines
18
24
  end
19
25
 
20
26
  attr_reader :lines
21
27
 
22
28
  private
23
29
 
24
- def initialize(columns, rows, attributes, max_width)
30
+ def initialize(columns, rows, att, max_width)
25
31
  @max_width, @columns = WidthFinder.find(columns, max_width)
26
- init_borders(attributes)
32
+ init_borders(att)
27
33
  @columns = @columns.each.with_index
34
+
28
35
  @lines = render(rows.shift)
29
36
  @lines.unshift(@b_top) if @b_top
30
- rows.each do |row|
31
- @lines << @b_between if @b_between
32
- @lines += render(row)
37
+
38
+ if @b_between
39
+ rows.each do |row|
40
+ @lines << @b_between
41
+ @lines += render(row)
42
+ end
43
+ else
44
+ rows.each { |row| @lines += render(row) }
33
45
  end
46
+
34
47
  @lines << @b_bottom if @b_bottom
35
48
  end
36
49
 
@@ -43,11 +56,12 @@ module NattyUI
43
56
  end
44
57
  end
45
58
 
46
- def init_borders(attributes)
47
- chars = attributes.border_chars or return
48
- style = border_style(attributes)
59
+ def init_borders(att)
60
+ chars = att.border_chars or return
61
+ style = border_style(att)
49
62
  @b_inner = style[chars[9]]
50
- return init_borders_around(chars, style) if attributes.border_around
63
+ return if chars[10] == ' '
64
+ return init_borders_around(chars, style) if att.border_around
51
65
  @b_between = chars[10] * (@max_width + @columns.size - 1)
52
66
  i = -1
53
67
  @columns[0..-2].each { |w| @b_between[i += w + 1] = chars[4] }
@@ -71,8 +85,8 @@ module NattyUI
71
85
  @b_outer = @b_inner
72
86
  end
73
87
 
74
- def border_style(attributes)
75
- style = attributes.border_style_bbcode
88
+ def border_style(att)
89
+ style = att.border_style_bbcode
76
90
  style ? ->(line) { "#{style}#{line}[/]" } : lambda(&:itself)
77
91
  end
78
92
 
data/lib/natty-ui/task.rb CHANGED
@@ -3,40 +3,44 @@
3
3
  require_relative 'element'
4
4
 
5
5
  module NattyUI
6
- # {Element} implemting a task display section used by {Features.task}.
6
+ # Task display section used by {Features.task}.
7
7
  #
8
8
  class Task < Temporary
9
- include StateMixin
9
+ include WithStatus
10
10
 
11
11
  private
12
12
 
13
- def finish_ok(text)
14
- NattyUI.back_to_line(@start_line)
13
+ def _done(text)
14
+ NattyUI.back_to_line(@start_line, erase: :all)
15
15
  @start_line = nil
16
16
  cm = Theme.current.mark(:checkmark)
17
- text << @title if text.empty?
18
17
  @parent.puts(
19
18
  *text,
20
19
  first_line_prefix: cm,
21
20
  first_line_prefix_width: cm.width,
22
21
  pin: @pin
23
22
  )
24
- super()
23
+ @pins&.each { |objects, options| puts(*objects, **options) }
24
+ end
25
+
26
+ def _failed
27
+ @start_line = nil
25
28
  end
26
29
 
27
30
  def initialize(parent, title, msg, pin)
28
31
  super(parent)
29
32
  @title = title
30
- @prefix = '  '
31
- @prefix_width = 2
32
33
  @pin = pin
33
34
  style = Theme.current.task_style
35
+ cm = Theme.current.mark(:current)
36
+ @prefix = ' ' * cm.width
37
+ @prefix_width = cm.width
34
38
  parent.puts(
35
39
  title,
36
- first_line_prefix: "#{style}➔ ",
37
- first_line_prefix_width: 2,
38
- prefix: "#{style}  ",
39
- prefix_width: 2
40
+ first_line_prefix: "#{cm}#{style}",
41
+ first_line_prefix_width: cm.width,
42
+ prefix: "#{@prefix}#{style}",
43
+ prefix_width: cm.width
40
44
  )
41
45
  puts(*msg) unless msg.empty?
42
46
  end
@@ -3,7 +3,7 @@
3
3
  require_relative 'element'
4
4
 
5
5
  module NattyUI
6
- # {Element} implemting a temprary display section used by
6
+ # Temprary display section used by
7
7
  # {Features.temporary}.
8
8
  #
9
9
  class Temporary < Element
@@ -20,7 +20,7 @@ module NattyUI
20
20
  # @!visibility private
21
21
  def done
22
22
  return self if @state
23
- NattyUI.back_to_line(@start_line) if @start_line
23
+ NattyUI.back_to_line(@start_line, erase: :all) if @start_line
24
24
  @pins&.each { |objects, options| puts(*objects, **options) }
25
25
  @state = :ok
26
26
  self
@@ -28,8 +28,6 @@ module NattyUI
28
28
 
29
29
  private
30
30
 
31
- alias finish_ok done
32
-
33
31
  def initialize(parent)
34
32
  @start_line = NattyUI.lines_written
35
33
  super
@@ -53,6 +53,7 @@ module NattyUI
53
53
  warning: '[bright_yellow]![/fg]',
54
54
  error: '[red]𝙓[/fg]',
55
55
  failed: '[bright_red]𝑭[/fg]',
56
+ current: '[bright_green]➔[/fg]',
56
57
  choice: '[bright_white]◦[/fg]',
57
58
  current_choice: '[bright_green]◉[/fg]'
58
59
  )
@@ -66,6 +67,35 @@ module NattyUI
66
67
  )
67
68
  end
68
69
  end
70
+
71
+ def emoji
72
+ create do |theme|
73
+ theme.heading_sytle = :bright_blue
74
+ theme.task_style = %i[bright_green b]
75
+ # theme.choice_style =
76
+ theme.choice_current_style = %i[bright_white on_blue b]
77
+ theme.define_marker(
78
+ bullet: '▫️',
79
+ checkmark: '✅',
80
+ quote: '[bright_blue]▍[/fg]',
81
+ information: '📌',
82
+ warning: '⚠️',
83
+ error: '❗️',
84
+ failed: '‼️',
85
+ current: '➡️',
86
+ choice: '[bright_white]•[/fg]',
87
+ current_choice: '[bright_green]●[/fg]'
88
+ )
89
+ theme.define_section(
90
+ default: :bright_blue,
91
+ message: :bright_blue,
92
+ information: :bright_blue,
93
+ warning: :bright_yellow,
94
+ error: :red,
95
+ failed: :bright_red
96
+ )
97
+ end
98
+ end
69
99
  end
70
100
 
71
101
  attr_accessor :section_border
@@ -206,7 +236,7 @@ module NattyUI
206
236
  return create_styled_heading(heading, style) if style
207
237
  heading.map do |left|
208
238
  right = " #{left.reverse}"
209
- [left = Str.new("#{left} ", true), Str.new(right, left.size)]
239
+ [left = Str.new("#{left} ", true), Str.new(right, left.width)]
210
240
  end
211
241
  end
212
242
 
@@ -215,7 +245,7 @@ module NattyUI
215
245
  right = Ansi.decorate(left.reverse, *style)
216
246
  [
217
247
  left = Str.new("#{Ansi.decorate(left, *style)} ", true),
218
- Str.new(" #{right}", left.size)
248
+ Str.new(" #{right}", left.width)
219
249
  ]
220
250
  end
221
251
  end
@@ -278,6 +308,7 @@ module NattyUI
278
308
  warning: '!',
279
309
  error: '𝙓',
280
310
  failed: '𝑭',
311
+ current: '➔',
281
312
  choice: '◦',
282
313
  current_choice: '◉'
283
314
  }
@@ -24,12 +24,22 @@ module NattyUI
24
24
 
25
25
  # @!visibility private
26
26
  def align(value)
27
- value == :right || value == :centered ? value : :left
27
+ POS_ALI.include?(value) ? value : :left
28
+ end
29
+
30
+ # @!visibility private
31
+ def position(value)
32
+ value if POS_ALI.include?(value)
28
33
  end
29
34
 
30
35
  # @!visibility private
31
36
  def vertical(value)
32
- value == :bottom || value == :middle ? value : :top
37
+ VERT.include?(value) ? value : :top
38
+ end
39
+
40
+ # @!visibility private
41
+ def split_table_attr(values)
42
+ [values.slice(*TAB_ATTR), values.except(*TAB_ATTR)]
33
43
  end
34
44
 
35
45
  # @!visibility private
@@ -39,7 +49,7 @@ module NattyUI
39
49
  when 0
40
50
  [0, 0, 0, 0]
41
51
  when 1
42
- [value[0], 0, 0, 0]
52
+ Array.new(4, value[0])
43
53
  when 2
44
54
  value * 2
45
55
  when 3
@@ -50,27 +60,29 @@ module NattyUI
50
60
  end
51
61
  alias margin padding
52
62
  end
63
+
64
+ POS_ALI = %i[right centered].freeze
65
+ VERT = %i[bottom middle].freeze
66
+ TAB_ATTR = %i[border_around border border_style position].freeze
53
67
  end
54
68
 
55
69
  class Str
56
70
  attr_reader :to_s
57
-
58
71
  alias to_str to_s
59
- def inspect = to_s.inspect
60
- def empty? = size == 0
72
+ def empty? = width == 0
73
+ def inspect = @to_s.inspect
61
74
 
62
- def size
63
- return @size if @size
64
- @size = Text.width(self)
75
+ def width
76
+ return @width if @width
77
+ @width = Text.width(self)
65
78
  freeze
66
- @size
79
+ @width
67
80
  end
68
- alias width size
69
81
 
70
- def initialize(str, size = nil)
82
+ def initialize(str, width = nil)
71
83
  @to_s = Ansi.bbcode(str).freeze
72
- return unless size
73
- @size = @size.is_a?(Integer) ? size : Text.width(self)
84
+ return unless width
85
+ @width = @width.is_a?(Integer) ? width : Text.width(self)
74
86
  freeze
75
87
  end
76
88
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # The version number of the gem.
5
- VERSION = '0.25.0'
5
+ VERSION = '0.27.0'
6
6
  end
@@ -43,9 +43,9 @@ module NattyUI
43
43
 
44
44
  def fit_max_width(columns, max_width, saving_by_column)
45
45
  ret = max_width - columns.size + 1
46
+ columns = columns.map { normalized(_1, max_width) }
46
47
  return columns, ret if ret >= 1
47
48
  saving_by_column += 1
48
- columns = columns.dup
49
49
  until ret >= 1
50
50
  columns.pop
51
51
  ret += saving_by_column
@@ -53,6 +53,16 @@ module NattyUI
53
53
  [columns, ret]
54
54
  end
55
55
 
56
+ def normalized(value, max_width)
57
+ return (max_width * value).to_i if value.is_a?(Float)
58
+ return value unless value.is_a?(Range)
59
+ min = value.begin
60
+ max = value.end
61
+ min = (max_width * min).to_i if min.is_a?(Float)
62
+ max = (max_width * max).to_i if max.is_a?(Float)
63
+ (min..max)
64
+ end
65
+
56
66
  def expand(sum)
57
67
  max_width = [@columns.sum(&:max_val), @max_width].min
58
68
  expandables = @columns.find_all(&:expandable?)
data/lib/natty-ui.rb CHANGED
@@ -58,12 +58,12 @@ module NattyUI
58
58
  def title=(value)
59
59
  if value
60
60
  title = Ansi.plain(value).gsub(/\s+/, ' ')
61
- _write(Ansi.tab_title(title)) if Terminal.ansi?
61
+ _write(Ansi.title(title)) if Terminal.ansi?
62
62
  @title_stack << title
63
63
  else
64
64
  @title_stack.pop
65
65
  last = @title_stack[-1]
66
- _write(Ansi.tab_title(last)) if last && Terminal.ansi?
66
+ _write(Ansi.title(last)) if last && Terminal.ansi?
67
67
  end
68
68
  end
69
69
 
@@ -74,7 +74,13 @@ module NattyUI
74
74
  def back_to_line(number, erase: true)
75
75
  return @lines_written if (c = @lines_written - number) <= 0
76
76
  if Terminal.ansi?
77
- _write(erase ? (Ansi::LINE_ERASE_PREV * c) : Ansi.cursor_prev_line(c))
77
+ _write(
78
+ if erase == :all
79
+ Ansi.cursor_prev_line(c) + Ansi::SCREEN_ERASE_BELOW
80
+ else
81
+ erase ? (Ansi::LINE_ERASE_PREV * c) : Ansi.cursor_prev_line(c)
82
+ end
83
+ )
78
84
  end
79
85
  @lines_written = number
80
86
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natty-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.0
4
+ version: 0.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt
@@ -13,16 +13,16 @@ dependencies:
13
13
  name: terminal_rb
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - ">"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.9.5
18
+ version: 0.11.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - ">"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 0.9.5
25
+ version: 0.11.0
26
26
  description: |
27
27
  This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely,
28
28
  natty user interface tool you like to have for your command line applications.
@@ -45,6 +45,8 @@ files:
45
45
  - examples/elements.rb
46
46
  - examples/examples.rb
47
47
  - examples/illustration.rb
48
+ - examples/info.rb
49
+ - examples/key-codes.rb
48
50
  - examples/ls.rb
49
51
  - examples/named-colors.rb
50
52
  - examples/sections.rb
@@ -76,7 +78,7 @@ metadata:
76
78
  rubygems_mfa_required: 'true'
77
79
  source_code_uri: https://github.com/mblumtritt/natty-ui
78
80
  bug_tracker_uri: https://github.com/mblumtritt/natty-ui/issues
79
- documentation_uri: https://rubydoc.info/gems/natty-ui/0.25.0/NattyUI/
81
+ documentation_uri: https://rubydoc.info/gems/natty-ui/0.27.0/NattyUI
80
82
  rdoc_options: []
81
83
  require_paths:
82
84
  - lib
@@ -91,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
93
  - !ruby/object:Gem::Version
92
94
  version: '0'
93
95
  requirements: []
94
- rubygems_version: 3.6.9
96
+ rubygems_version: 3.7.1
95
97
  specification_version: 4
96
98
  summary: This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely,
97
99
  natty user interface you like to have for your CLI.