natty-ui 0.7.0 → 0.9.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/.yardopts +7 -3
- data/README.md +25 -47
- data/examples/24bit-colors.rb +27 -0
- data/examples/3bit-colors.rb +14 -0
- data/examples/8bit-colors.rb +31 -0
- data/examples/animate.rb +24 -0
- data/examples/attributes.rb +25 -159
- data/examples/demo.rb +53 -0
- data/examples/illustration.png +0 -0
- data/examples/illustration.rb +29 -0
- data/examples/{list_in_columns.rb → ls.rb} +13 -20
- data/examples/message.rb +30 -0
- data/examples/progress.rb +25 -30
- data/examples/query.rb +26 -28
- data/examples/read_key.rb +13 -0
- data/examples/table.rb +36 -0
- data/lib/natty-ui/ansi.rb +351 -305
- data/lib/natty-ui/ansi_constants.rb +73 -0
- data/lib/natty-ui/ansi_wrapper.rb +136 -162
- data/lib/natty-ui/features.rb +11 -18
- data/lib/natty-ui/key_map.rb +119 -0
- data/lib/natty-ui/line_animation/default.rb +35 -0
- data/lib/natty-ui/line_animation/matrix.rb +28 -0
- data/lib/natty-ui/line_animation/rainbow.rb +30 -0
- data/lib/natty-ui/line_animation/test.rb +29 -0
- data/lib/natty-ui/line_animation/type_writer.rb +64 -0
- data/lib/natty-ui/line_animation.rb +54 -0
- data/lib/natty-ui/version.rb +2 -2
- data/lib/natty-ui/wrapper/animate.rb +17 -0
- data/lib/natty-ui/wrapper/ask.rb +21 -21
- data/lib/natty-ui/wrapper/element.rb +19 -23
- data/lib/natty-ui/wrapper/framed.rb +29 -19
- data/lib/natty-ui/wrapper/heading.rb +26 -53
- data/lib/natty-ui/wrapper/horizontal_rule.rb +37 -0
- data/lib/natty-ui/wrapper/list_in_columns.rb +71 -12
- data/lib/natty-ui/wrapper/message.rb +20 -27
- data/lib/natty-ui/wrapper/progress.rb +40 -13
- data/lib/natty-ui/wrapper/query.rb +34 -31
- data/lib/natty-ui/wrapper/quote.rb +25 -0
- data/lib/natty-ui/wrapper/request.rb +21 -10
- data/lib/natty-ui/wrapper/section.rb +55 -39
- data/lib/natty-ui/wrapper/table.rb +298 -0
- data/lib/natty-ui/wrapper/task.rb +6 -7
- data/lib/natty-ui/wrapper.rb +123 -41
- data/lib/natty-ui.rb +65 -40
- metadata +28 -9
- data/examples/basic.rb +0 -62
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NattyUI
|
4
|
+
module LineAnimation
|
5
|
+
class Rainbow < None
|
6
|
+
def initialize(*_)
|
7
|
+
super
|
8
|
+
@prefix = to_column
|
9
|
+
end
|
10
|
+
|
11
|
+
def print(line)
|
12
|
+
line = plain(line)
|
13
|
+
11.upto(200) do |spread|
|
14
|
+
(
|
15
|
+
@stream << @prefix <<
|
16
|
+
Ansi.rainbow(
|
17
|
+
line,
|
18
|
+
frequence: 0.1,
|
19
|
+
spread: spread / 100.0,
|
20
|
+
seed: 0
|
21
|
+
)
|
22
|
+
).flush
|
23
|
+
sleep(0.01)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
define rainbow: Rainbow
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NattyUI
|
4
|
+
module LineAnimation
|
5
|
+
class Test < None
|
6
|
+
def initialize(*_)
|
7
|
+
super
|
8
|
+
@color = attribute(:color, :default)
|
9
|
+
end
|
10
|
+
|
11
|
+
def print(line, column)
|
12
|
+
prefix = "#{Ansi.cursor_column(column)}#{@color}"
|
13
|
+
line = plain(line)
|
14
|
+
str = Array.new(line.size) { CHARS.sample }.join
|
15
|
+
pos = Array.new(line.size, &:itself).shuffle
|
16
|
+
until pos.size < 4
|
17
|
+
pos.shift(pos.size / 4).each { str[_1] = line[_1] }
|
18
|
+
pos.sample(pos.size / 2).each { str[_1] = CHARS.sample }
|
19
|
+
(@stream << "#{prefix}#{str}").flush
|
20
|
+
sleep(0.08)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
CHARS = '2598Z*):.\=+-¦|_ヲアウエオカキケコサシスセソタツテナニヌネハヒホマミムメモヤユラリワ'.chars
|
25
|
+
end
|
26
|
+
|
27
|
+
define test: Test
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NattyUI
|
4
|
+
module LineAnimation
|
5
|
+
class TypeWriter < None
|
6
|
+
def initialize(*_)
|
7
|
+
super
|
8
|
+
@color = color
|
9
|
+
@cursor_color = attribute(:cursor_color, 0x2e)
|
10
|
+
@column = @options[:prefix_width] + 1
|
11
|
+
@num = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def print(line)
|
15
|
+
line = plain(line)
|
16
|
+
if (@num += 1).odd?
|
17
|
+
line.each_char do |char|
|
18
|
+
cursor(char)
|
19
|
+
(@stream << char).flush
|
20
|
+
end
|
21
|
+
else
|
22
|
+
pos = @column + line.size
|
23
|
+
line.reverse!
|
24
|
+
line.each_char do |char|
|
25
|
+
@stream << Ansi.cursor_column(pos -= 1)
|
26
|
+
cursor(char)
|
27
|
+
(@stream << char).flush
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cursor(char)
|
33
|
+
return sleep(0.016) if SPACE.match?(char)
|
34
|
+
@stream << @cursor_color
|
35
|
+
'▁▂▃▄▅▆▇█'.each_char do |cursor|
|
36
|
+
(@stream << cursor).flush
|
37
|
+
sleep(0.002)
|
38
|
+
@stream << CURSOR_BACK
|
39
|
+
end
|
40
|
+
@stream << @color
|
41
|
+
end
|
42
|
+
|
43
|
+
def print_org(line)
|
44
|
+
plain(line).each_char do |char|
|
45
|
+
if SPACE.match?(char)
|
46
|
+
sleep(0.016)
|
47
|
+
else
|
48
|
+
@stream << @cursor_color
|
49
|
+
'▁▂▃▄▅▆▇█'.each_char do |cursor|
|
50
|
+
(@stream << cursor).flush
|
51
|
+
sleep(0.002)
|
52
|
+
@stream << CURSOR_BACK
|
53
|
+
end
|
54
|
+
end
|
55
|
+
(@stream << @color << char).flush
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
CURSOR_BACK = Ansi.cursor_back(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
define type_writer: TypeWriter
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'ansi'
|
4
|
+
|
5
|
+
module NattyUI
|
6
|
+
module LineAnimation
|
7
|
+
def self.defined = @defined.keys
|
8
|
+
def self.defined?(name) = @defined.key?(name)
|
9
|
+
def self.define(**kwargs) = @defined.merge!(kwargs)
|
10
|
+
|
11
|
+
def self.[](name)
|
12
|
+
return if name.nil?
|
13
|
+
klass = @defined[name] || @defined[:default]
|
14
|
+
return klass unless klass.is_a?(String)
|
15
|
+
require(klass)
|
16
|
+
klass = @defined[name] and return klass
|
17
|
+
raise(LoadError, "unknown animation - #{name}")
|
18
|
+
end
|
19
|
+
|
20
|
+
class None
|
21
|
+
def initialize(stream, options)
|
22
|
+
@stream = stream
|
23
|
+
@options = options
|
24
|
+
end
|
25
|
+
|
26
|
+
def print(_line) = nil
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def to_column = Ansi.cursor_column(@options[:prefix_width] + 1)
|
31
|
+
def color = attribute(:color, :default)
|
32
|
+
def plain(str) = NattyUI.plain(str, ansi: false)
|
33
|
+
|
34
|
+
def attribute(name, *default)
|
35
|
+
att = @options[name] or return Ansi[*default]
|
36
|
+
return Ansi[*att] if att.is_a?(Enumerable)
|
37
|
+
Ansi.try_convert(att.to_s) || Ansi[*default]
|
38
|
+
end
|
39
|
+
|
40
|
+
SPACE = /[[:space:]]/
|
41
|
+
end
|
42
|
+
|
43
|
+
dir = __dir__
|
44
|
+
@defined = {
|
45
|
+
default: "#{dir}/line_animation/default",
|
46
|
+
matrix: "#{dir}/line_animation/matrix",
|
47
|
+
rainbow: "#{dir}/line_animation/rainbow",
|
48
|
+
type_writer: "#{dir}/line_animation/type_writer",
|
49
|
+
test: "#{dir}/line_animation/test"
|
50
|
+
}.compare_by_identity
|
51
|
+
end
|
52
|
+
|
53
|
+
private_constant :LineAnimation
|
54
|
+
end
|
data/lib/natty-ui/version.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NattyUI
|
4
|
+
module Features
|
5
|
+
# Print given arguments line-wise with animation.
|
6
|
+
#
|
7
|
+
# @overload animate(..., animation: :default)
|
8
|
+
# @param [#to_s] ... objects to print
|
9
|
+
# @param [:default, :matrix, :rainbow, :type_writer]
|
10
|
+
# animation type of animation
|
11
|
+
# @return [Wrapper::Section, Wrapper] it's parent object
|
12
|
+
def animate(*args, **kwargs)
|
13
|
+
kwargs[:animation] ||= :default
|
14
|
+
puts(*args, **kwargs)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/natty-ui/wrapper/ask.rb
CHANGED
@@ -13,13 +13,13 @@ module NattyUI
|
|
13
13
|
# The default for `yes` includes `ENTER` and `RETURN` key
|
14
14
|
#
|
15
15
|
# @example
|
16
|
-
# case
|
16
|
+
# case ui.ask('Do you like the NattyUI gem?')
|
17
17
|
# when true
|
18
|
-
#
|
18
|
+
# ui.info('Yeah!!')
|
19
19
|
# when false
|
20
|
-
#
|
20
|
+
# ui.write("That's pity!")
|
21
21
|
# else
|
22
|
-
#
|
22
|
+
# ui.failed('You should have an opinion!')
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# @see NattyUI.in_stream
|
@@ -28,7 +28,7 @@ module NattyUI
|
|
28
28
|
# @param yes [#to_s] chars which will be used to answer 'Yes'
|
29
29
|
# @param no [#to_s] chars which will be used to answer 'No'
|
30
30
|
# @return [Boolean] whether the answer is yes or no
|
31
|
-
# @return [nil] when input was aborted with
|
31
|
+
# @return [nil] when input was aborted with `^C` or `^D`
|
32
32
|
def ask(question, yes: "jotsyd\r\n", no: 'n')
|
33
33
|
_element(:Ask, question, yes, no)
|
34
34
|
end
|
@@ -42,27 +42,27 @@ module NattyUI
|
|
42
42
|
class Ask < Element
|
43
43
|
protected
|
44
44
|
|
45
|
-
def
|
45
|
+
def call(question, yes, no)
|
46
46
|
yes, no = grab(yes, no)
|
47
|
-
|
48
|
-
read(yes, no)
|
49
|
-
ensure
|
50
|
-
finish
|
51
|
-
end
|
52
|
-
|
53
|
-
def query(question)
|
54
|
-
(wrapper.stream << prefix << "▶︎ #{question} ").flush
|
55
|
-
end
|
56
|
-
|
57
|
-
def finish = (wrapper.stream << "\n").flush
|
58
|
-
|
59
|
-
def read(yes, no)
|
47
|
+
draw(question)
|
60
48
|
while true
|
61
|
-
char = NattyUI.
|
62
|
-
return if "\3\4
|
49
|
+
char = NattyUI.read_key(mode: :raw)
|
50
|
+
return if "\3\4".include?(char)
|
63
51
|
return true if yes.include?(char)
|
64
52
|
return false if no.include?(char)
|
65
53
|
end
|
54
|
+
ensure
|
55
|
+
wrapper.ansi? ? (wrapper.stream << Ansi::CLL).flush : @parent.puts
|
56
|
+
end
|
57
|
+
|
58
|
+
def draw(question)
|
59
|
+
glyph = wrapper.glyph(:query)
|
60
|
+
@parent.print(
|
61
|
+
question,
|
62
|
+
prefix: "#{glyph} #{Ansi[255]}",
|
63
|
+
prefix_width: NattyUI.display_width(glyph) + 1,
|
64
|
+
suffix_width: 0
|
65
|
+
)
|
66
66
|
end
|
67
67
|
|
68
68
|
def grab(yes, no)
|
@@ -10,7 +10,7 @@ module NattyUI
|
|
10
10
|
class Element
|
11
11
|
include Features
|
12
12
|
|
13
|
-
# @return [
|
13
|
+
# @return [Element] when embedded in a section
|
14
14
|
# @return [Wrapper] when not embedded in a section
|
15
15
|
attr_reader :parent
|
16
16
|
|
@@ -22,6 +22,9 @@ module NattyUI
|
|
22
22
|
# @return [Boolean] whether its closed or not
|
23
23
|
def closed? = (@status != nil)
|
24
24
|
|
25
|
+
# @return [Integer] available columns count within the element
|
26
|
+
def available_width = @parent.available_width
|
27
|
+
|
25
28
|
# Close the element.
|
26
29
|
#
|
27
30
|
# @return [Element] itself
|
@@ -29,28 +32,11 @@ module NattyUI
|
|
29
32
|
|
30
33
|
alias _to_s to_s
|
31
34
|
private :_to_s
|
32
|
-
|
33
35
|
# @!visibility private
|
34
|
-
def inspect =
|
36
|
+
def inspect = _to_s
|
35
37
|
|
36
38
|
protected
|
37
39
|
|
38
|
-
def prefix = "#{@parent.__send__(:prefix)}#{@prefix}"
|
39
|
-
def suffix = "#{@suffix}#{@parent.__send__(:suffix)}"
|
40
|
-
def prefix_width = _blemish_width(prefix)
|
41
|
-
def suffix_width = _blemish_width(suffix)
|
42
|
-
def available_width = wrapper.screen_columns - prefix_width - suffix_width
|
43
|
-
def finish = nil
|
44
|
-
|
45
|
-
def wrapper
|
46
|
-
return @wrapper if @wrapper
|
47
|
-
@wrapper = self
|
48
|
-
@wrapper = @wrapper.parent until @wrapper.is_a?(Wrapper)
|
49
|
-
@wrapper
|
50
|
-
end
|
51
|
-
|
52
|
-
def initialize(parent, **_) = (@parent = parent)
|
53
|
-
|
54
40
|
def _close(state)
|
55
41
|
return self if @status
|
56
42
|
@status = state
|
@@ -58,18 +44,28 @@ module NattyUI
|
|
58
44
|
@raise ? raise(BREAK) : self
|
59
45
|
end
|
60
46
|
|
61
|
-
def
|
47
|
+
def call
|
48
|
+
NattyUI.instance_variable_set(:@element, self)
|
62
49
|
@raise = true
|
63
50
|
yield(self)
|
64
|
-
|
51
|
+
closed? ? self : close
|
65
52
|
rescue BREAK
|
66
53
|
nil
|
54
|
+
ensure
|
55
|
+
NattyUI.instance_variable_set(:@element, @parent)
|
67
56
|
end
|
68
57
|
|
69
|
-
|
70
|
-
|
58
|
+
def finish = nil
|
59
|
+
def prefix = "#{@parent.instance_variable_get(:@prefix)}#{@prefix}"
|
60
|
+
|
61
|
+
def initialize(parent)
|
62
|
+
@parent = parent
|
63
|
+
end
|
71
64
|
|
72
65
|
private_class_method :new
|
66
|
+
|
67
|
+
BREAK = Class.new(StandardError)
|
68
|
+
private_constant :BREAK
|
73
69
|
end
|
74
70
|
end
|
75
71
|
end
|
@@ -10,15 +10,13 @@ module NattyUI
|
|
10
10
|
# When no block is given, the section must be closed, see
|
11
11
|
# {Wrapper::Element#close}.
|
12
12
|
#
|
13
|
-
# @param [#to_s] title object to print as section title
|
14
13
|
# @param [Array<#to_s>] args more objects to print
|
15
|
-
# @param [
|
16
|
-
# valid types are `:rounded`, `:simple`, `:heavy`, `:semi`, `:double`
|
14
|
+
# @param [:block, :double, :heavy, :rounded, :semi, :simple] type frame type
|
17
15
|
# @yieldparam [Wrapper::Framed] framed the created section
|
18
16
|
# @return [Object] the result of the code block
|
19
17
|
# @return [Wrapper::Framed] itself, when no code block is given
|
20
|
-
def framed(
|
21
|
-
_section(
|
18
|
+
def framed(*args, type: :rounded, &block)
|
19
|
+
_section(:Framed, args, type: type, &block)
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
@@ -30,25 +28,37 @@ module NattyUI
|
|
30
28
|
class Framed < Section
|
31
29
|
protected
|
32
30
|
|
33
|
-
def initialize(parent,
|
34
|
-
|
35
|
-
parent
|
36
|
-
|
37
|
-
super(parent, prefix: "#{left} ", **opts)
|
31
|
+
def initialize(parent, type:)
|
32
|
+
deco = as_deco(type)
|
33
|
+
super(parent, prefix: "#{deco[0]} ", prefix_width: 2, suffix_width: 2)
|
34
|
+
init(deco)
|
38
35
|
end
|
39
36
|
|
40
|
-
def
|
37
|
+
def as_deco(type)
|
38
|
+
if type.is_a?(Symbol)
|
39
|
+
ret = DECO[type] and return ret
|
40
|
+
elsif type.is_a?(String)
|
41
|
+
return type if type.size == 8
|
42
|
+
return type * 8 if type.size == 1
|
43
|
+
end
|
44
|
+
raise(ArgumentError, "invalid frame type - #{type.inspect}")
|
45
|
+
end
|
41
46
|
|
42
|
-
def
|
43
|
-
|
47
|
+
def init(deco)
|
48
|
+
aw = @parent.available_width - 1
|
49
|
+
parent.puts("#{deco[1]}#{deco[2] * aw}")
|
50
|
+
@finish = "#{deco[5]}#{deco[6] * aw}"
|
44
51
|
end
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
def finish = @parent.puts(@finish)
|
54
|
+
|
55
|
+
DECO = {
|
56
|
+
rounded: '│╭─╮│╰─╯',
|
57
|
+
simple: '│┌─┐│└─┘',
|
58
|
+
heavy: '┃┏━┓┃┗━┛',
|
59
|
+
double: '║╔═╗║╚═╝',
|
60
|
+
semi: '│╒═╕│╘═╛',
|
61
|
+
block: '▌▛▀▜▐▙▄▟'
|
52
62
|
}.compare_by_identity.freeze
|
53
63
|
end
|
54
64
|
end
|
@@ -1,87 +1,60 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'element'
|
4
4
|
|
5
5
|
module NattyUI
|
6
6
|
module Features
|
7
|
-
#
|
7
|
+
# Prints a H1 title.
|
8
8
|
#
|
9
|
-
# @param
|
10
|
-
# @
|
11
|
-
|
12
|
-
# @return [Wrapper::Heading] itself, when no code block is given
|
13
|
-
def h1(title, *args, &block)
|
14
|
-
_section(self, :Heading, args, title: title, weight: 1, &block)
|
15
|
-
end
|
9
|
+
# @param [#to_s] title text
|
10
|
+
# @return [Wrapper::Section, Wrapper] it's parent object
|
11
|
+
def h1(title) = _element(:Heading, title, '═══════')
|
16
12
|
|
17
|
-
#
|
13
|
+
# Prints a H2 title.
|
18
14
|
#
|
19
|
-
# @param (see #
|
20
|
-
# @yieldparam (see #h1)
|
15
|
+
# @param (see #h1)
|
21
16
|
# @return (see #h1)
|
22
|
-
def h2(title,
|
23
|
-
_section(self, :Heading, args, title: title, weight: 2, &block)
|
24
|
-
end
|
17
|
+
def h2(title) = _element(:Heading, title, '━━━━━')
|
25
18
|
|
26
|
-
#
|
19
|
+
# Prints a H3 title.
|
27
20
|
#
|
28
|
-
# @param (see #
|
29
|
-
# @yieldparam (see #h1)
|
21
|
+
# @param (see #h1)
|
30
22
|
# @return (see #h1)
|
31
|
-
def h3(title,
|
32
|
-
_section(self, :Heading, args, title: title, weight: 3, &block)
|
33
|
-
end
|
23
|
+
def h3(title) = _element(:Heading, title, '━━━')
|
34
24
|
|
35
|
-
#
|
25
|
+
# Prints a H4 title.
|
36
26
|
#
|
37
|
-
# @param (see #
|
38
|
-
# @yieldparam (see #h1)
|
27
|
+
# @param (see #h1)
|
39
28
|
# @return (see #h1)
|
40
|
-
def h4(title,
|
41
|
-
_section(self, :Heading, args, title: title, weight: 4, &block)
|
42
|
-
end
|
29
|
+
def h4(title) = _element(:Heading, title, '───')
|
43
30
|
|
44
|
-
#
|
31
|
+
# Prints a H5 title.
|
45
32
|
#
|
46
|
-
# @param (see #
|
47
|
-
# @yieldparam (see #h1)
|
33
|
+
# @param (see #h1)
|
48
34
|
# @return (see #h1)
|
49
|
-
def h5(title,
|
50
|
-
_section(self, :Heading, args, title: title, weight: 5, &block)
|
51
|
-
end
|
35
|
+
def h5(title) = _element(:Heading, title, '──')
|
52
36
|
end
|
53
37
|
|
54
38
|
class Wrapper
|
55
39
|
#
|
56
|
-
# A {
|
40
|
+
# A {Element} drawing a title.
|
57
41
|
#
|
58
42
|
# @see Features#h1
|
59
43
|
# @see Features#h2
|
60
44
|
# @see Features#h3
|
61
45
|
# @see Features#h4
|
62
46
|
# @see Features#h5
|
63
|
-
class Heading <
|
47
|
+
class Heading < Element
|
64
48
|
protected
|
65
49
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
enclose = ENCLOSE[weight]
|
74
|
-
return "#{enclose} ", " #{enclose}" if enclose
|
75
|
-
raise(ArgumentError, "invalid heading weight - #{weight}")
|
50
|
+
def call(title, enclose)
|
51
|
+
@parent.puts(
|
52
|
+
title,
|
53
|
+
prefix: "#{Ansi[39]}#{enclose} #{Ansi[:bold, 255]}",
|
54
|
+
suffix: " #{Ansi[:bold_off, 39]}#{enclose}#{Ansi::RESET}",
|
55
|
+
max_width: available_width - 2 - (enclose.size * 2)
|
56
|
+
)
|
76
57
|
end
|
77
|
-
|
78
|
-
ENCLOSE = {
|
79
|
-
1 => '═══════',
|
80
|
-
2 => '━━━━━',
|
81
|
-
3 => '━━━',
|
82
|
-
4 => '───',
|
83
|
-
5 => '──'
|
84
|
-
}.compare_by_identity.freeze
|
85
58
|
end
|
86
59
|
end
|
87
60
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'element'
|
4
|
+
|
5
|
+
module NattyUI
|
6
|
+
module Features
|
7
|
+
# Print a horizontal rule
|
8
|
+
#
|
9
|
+
# @param [#to_s] symbol string to build the horizontal rule
|
10
|
+
# @return [Wrapper::Section, Wrapper] it's parent object
|
11
|
+
def hr(symbol = '─') = _element(:HorizontalRule, symbol)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Wrapper
|
15
|
+
#
|
16
|
+
# A {Element} drawing a horizontal rule.
|
17
|
+
#
|
18
|
+
# @see Features#hr
|
19
|
+
class HorizontalRule < Element
|
20
|
+
protected
|
21
|
+
|
22
|
+
def call(symbol)
|
23
|
+
size = NattyUI.display_width(symbol = symbol.to_s)
|
24
|
+
return @parent.puts if size == 0
|
25
|
+
max_width = available_width
|
26
|
+
@parent.print(
|
27
|
+
symbol * ((max_width / size)),
|
28
|
+
max_width: max_width,
|
29
|
+
prefix: Ansi[39],
|
30
|
+
prefix_width: 0,
|
31
|
+
suffix: Ansi::RESET,
|
32
|
+
suffix_width: 0
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|