tty-box 0.2.1 → 0.6.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 +52 -0
- data/README.md +212 -33
- data/lib/tty/box.rb +291 -65
- data/lib/tty/box/border.rb +54 -14
- data/lib/tty/box/version.rb +1 -1
- metadata +31 -53
- data/Rakefile +0 -8
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/examples/commander.rb +0 -52
- data/spec/spec_helper.rb +0 -29
- data/spec/unit/align_spec.rb +0 -27
- data/spec/unit/border_spec.rb +0 -70
- data/spec/unit/frame_spec.rb +0 -49
- data/spec/unit/padding_spec.rb +0 -46
- data/spec/unit/position_spec.rb +0 -23
- data/spec/unit/style_spec.rb +0 -31
- data/spec/unit/title_spec.rb +0 -35
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-box.gemspec +0 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 66cd38e818e47ac372868586d8a76cdeb41a98aa9efc59430ec89acb84629410
|
|
4
|
+
data.tar.gz: d96e96b93e46a9ed8b7432632618af9c54e4da5f9575df820c6f77987104baf2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6bc7bddb63b580b8430d3dcdf596801db37e2f5813c12b8512a89dc9aff4140ebf6478e91d828020ffb182c84ca81b990d3d346c27718491c420a5a0099deca7
|
|
7
|
+
data.tar.gz: 43756dd006eec4de539d4a802c69afa04ca1b56fdd29505725d7001418a9e37f94005fc4606daa6be2564a8a12d2b876cbfb4d2ff1f0b43254c7580a99961b48
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Change log
|
|
2
2
|
|
|
3
|
+
## [v0.6.0] - 2020-08-11
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
* Change to preserve newline characters when wrapping content
|
|
7
|
+
* Change gemspec to include metadata and remove test files
|
|
8
|
+
* Change to update pastel & strings dependencies
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
* Fix Ruby 2.7 warnings
|
|
12
|
+
|
|
13
|
+
## [v0.5.0] - 2019-10-08
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
* Add ability to create frames without specifying width or height
|
|
17
|
+
* Add #info, #warn, #success, #error ready frames for status messages inspired by conversation with Konstantin Gredeskoul(@kigster)
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
* Change #frame to accept content as an argument in addition to a block
|
|
21
|
+
* Change to match titles with border styling
|
|
22
|
+
|
|
23
|
+
## [v0.4.1] - 2019-08-28
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
* Add example to demonstrate different line endings
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
* Fix to handle different line endings
|
|
30
|
+
|
|
31
|
+
## [v0.4.0] - 2019-06-05
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
* Change gemspec to require Ruby >= 2.0.0
|
|
35
|
+
* Change to update tty-cursor dependency
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
* Fix issue with displaying box with colored content
|
|
39
|
+
|
|
40
|
+
## [v0.3.0] - 2018-10-08
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
* Add border parameters :top_left, :top_right, :bottom_left & :bottom_right to allow specifying values for box corners
|
|
44
|
+
* Add :ascii border type for drawing ASCII boxes
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
* Fix box color fill to correctly recognise missing borders and match the height and width
|
|
48
|
+
* Fix absolute content positioning when borders are missing
|
|
49
|
+
|
|
3
50
|
## [v0.2.1] - 2018-09-10
|
|
4
51
|
|
|
5
52
|
### Fixed
|
|
@@ -15,6 +62,11 @@
|
|
|
15
62
|
|
|
16
63
|
* Initial implementation and release
|
|
17
64
|
|
|
65
|
+
[v0.6.0]: https://github.com/piotrmurach/tty-box/compare/v0.5.0...v0.6.0
|
|
66
|
+
[v0.5.0]: https://github.com/piotrmurach/tty-box/compare/v0.4.1...v0.5.0
|
|
67
|
+
[v0.4.1]: https://github.com/piotrmurach/tty-box/compare/v0.4.0...v0.4.1
|
|
68
|
+
[v0.4.0]: https://github.com/piotrmurach/tty-box/compare/v0.3.0...v0.4.0
|
|
69
|
+
[v0.3.0]: https://github.com/piotrmurach/tty-box/compare/v0.2.1...v0.3.0
|
|
18
70
|
[v0.2.1]: https://github.com/piotrmurach/tty-box/compare/v0.2.0...v0.2.1
|
|
19
71
|
[v0.2.0]: https://github.com/piotrmurach/tty-box/compare/v0.1.0...v0.2.0
|
|
20
72
|
[v0.1.0]: https://github.com/piotrmurach/tty-box/compare/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<a href="https://piotrmurach.github.io/tty" target="_blank"><img width="130" src="https://
|
|
2
|
+
<a href="https://piotrmurach.github.io/tty" target="_blank"><img width="130" src="https://github.com/piotrmurach/tty/blob/master/images/tty.png" alt="tty logo" /></a>
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
5
|
# TTY::Box [][gitter]
|
|
@@ -19,18 +19,18 @@
|
|
|
19
19
|
[coverage]: https://coveralls.io/github/piotrmurach/tty-box
|
|
20
20
|
[inchpages]: http://inch-ci.org/github/piotrmurach/tty-box
|
|
21
21
|
|
|
22
|
-
> Draw various frames and boxes in
|
|
22
|
+
> Draw various frames and boxes in the terminal window.
|
|
23
23
|
|
|
24
24
|
**TTY::Box** provides box drawing component for [TTY](https://github.com/piotrmurach/tty) toolkit.
|
|
25
25
|
|
|
26
|
-

|
|
27
27
|
|
|
28
28
|
## Installation
|
|
29
29
|
|
|
30
30
|
Add this line to your application's Gemfile:
|
|
31
31
|
|
|
32
32
|
```ruby
|
|
33
|
-
gem
|
|
33
|
+
gem "tty-box"
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
And then execute:
|
|
@@ -52,49 +52,90 @@ Or install it yourself as:
|
|
|
52
52
|
* [2.5 border](#25-border)
|
|
53
53
|
* [2.6 styling](#26-styling)
|
|
54
54
|
* [2.7 formatting](#27-formatting)
|
|
55
|
+
* [2.8 messages](#28-messages)
|
|
56
|
+
* [2.8.1 info](#281-info)
|
|
57
|
+
* [2.8.2 warn](#282-warn)
|
|
58
|
+
* [2.8.3 success](#283-success)
|
|
59
|
+
* [2.8.4 error](#284-error)
|
|
55
60
|
|
|
56
61
|
## 1. Usage
|
|
57
62
|
|
|
58
63
|
Using the `frame` method, you can draw a box in a terminal emulator:
|
|
59
64
|
|
|
60
65
|
```ruby
|
|
61
|
-
box = TTY::Box.frame
|
|
62
|
-
width: 30,
|
|
63
|
-
height: 10,
|
|
64
|
-
align: :center,
|
|
65
|
-
padding: 3
|
|
66
|
-
) do
|
|
67
|
-
"Drawing a box in terminal emulator"
|
|
68
|
-
end
|
|
69
|
-
|
|
66
|
+
box = TTY::Box.frame "Drawing a box in", "terminal emulator", padding: 3, align: :center
|
|
70
67
|
```
|
|
71
68
|
|
|
69
|
+
And then print:
|
|
70
|
+
|
|
72
71
|
```ruby
|
|
73
72
|
print box
|
|
74
73
|
# =>
|
|
75
|
-
#
|
|
76
|
-
# │
|
|
77
|
-
# │
|
|
78
|
-
# │
|
|
79
|
-
# │
|
|
80
|
-
# │
|
|
81
|
-
# │
|
|
82
|
-
# │
|
|
83
|
-
# │
|
|
84
|
-
#
|
|
74
|
+
# ┌───────────────────────┐
|
|
75
|
+
# │ │
|
|
76
|
+
# │ │
|
|
77
|
+
# │ │
|
|
78
|
+
# │ Drawing a box in │
|
|
79
|
+
# │ terminal emulator │
|
|
80
|
+
# │ │
|
|
81
|
+
# │ │
|
|
82
|
+
# │ │
|
|
83
|
+
# └───────────────────────┘
|
|
85
84
|
```
|
|
86
85
|
|
|
87
86
|
## 2. Interface
|
|
88
87
|
|
|
89
88
|
### 2.1 frame
|
|
90
89
|
|
|
91
|
-
You can draw a box in
|
|
90
|
+
You can draw a box in your terminal window by using the `frame` method and passing a content to display. By default the box will be drawn around the content.
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
print TTY::Box.frame "Hello world!"
|
|
94
|
+
# =>
|
|
95
|
+
# ┌────────────┐
|
|
96
|
+
# │Hello world!│
|
|
97
|
+
# └────────────┘
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
You can also provide multi line content as separate arguments.
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
print TTY::Box.frame "Hello", "world!"
|
|
104
|
+
# =>
|
|
105
|
+
# ┌──────┐
|
|
106
|
+
# │Hello │
|
|
107
|
+
# │world!│
|
|
108
|
+
# └──────┘
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Alternatively, provide a multi line content using newline chars in a single argument:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
print TTY::Box.frame "Hello\nworld!"
|
|
115
|
+
# =>
|
|
116
|
+
# ┌──────┐
|
|
117
|
+
# │Hello │
|
|
118
|
+
# │world!│
|
|
119
|
+
# └──────┘
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Finally, you can use a block to specify content:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
print TTY::Box.frame { "Hello world!" }
|
|
126
|
+
# =>
|
|
127
|
+
# ┌────────────┐
|
|
128
|
+
# │Hello world!│
|
|
129
|
+
# └────────────┘
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
You can also enforce a given box size without any content and use [tty-cursor](https://github.com/piotrmurach/tty-cursor) to position content whatever you like.
|
|
92
133
|
|
|
93
134
|
```ruby
|
|
94
135
|
box = TTY::Box.frame(width: 30, height: 10)
|
|
95
136
|
```
|
|
96
137
|
|
|
97
|
-
|
|
138
|
+
When printed will produce the following output in your terminal:
|
|
98
139
|
|
|
99
140
|
```ruby
|
|
100
141
|
print box
|
|
@@ -111,8 +152,6 @@ print box
|
|
|
111
152
|
# └────────────────────────────┘
|
|
112
153
|
```
|
|
113
154
|
|
|
114
|
-
Then you can use [tty-cursor](https://github.com/piotrmurach/tty-cursor) to directly manipulate content to be displayed inside the box.
|
|
115
|
-
|
|
116
155
|
Alternatively, you can also pass a block to provide a content for the box:
|
|
117
156
|
|
|
118
157
|
```ruby
|
|
@@ -121,7 +160,7 @@ box = TTY::Box.frame(width: 30, height: 10) do
|
|
|
121
160
|
end
|
|
122
161
|
```
|
|
123
162
|
|
|
124
|
-
|
|
163
|
+
When printed will produce the following output in your terminal:
|
|
125
164
|
|
|
126
165
|
```ruby
|
|
127
166
|
print box
|
|
@@ -172,7 +211,7 @@ You can specify titles using the `:title` keyword and a hash value that contains
|
|
|
172
211
|
|
|
173
212
|
|
|
174
213
|
```ruby
|
|
175
|
-
box = TTY::Box.frame(width: 30, height: 10, title: {top_left:
|
|
214
|
+
box = TTY::Box.frame(width: 30, height: 10, title: {top_left: "TITLE", bottom_right: "v1.0"})
|
|
176
215
|
```
|
|
177
216
|
|
|
178
217
|
which when printed in console will render the following:
|
|
@@ -194,10 +233,10 @@ print box
|
|
|
194
233
|
|
|
195
234
|
### 2.5 border
|
|
196
235
|
|
|
197
|
-
There are
|
|
236
|
+
There are three types of border `:ascii`, `:light`, `:thick`. By default the `:light` border is used. This can be changed using the `:border` keyword:
|
|
198
237
|
|
|
199
238
|
```ruby
|
|
200
|
-
box = TTY::Box.
|
|
239
|
+
box = TTY::Box.frame(width: 30, height: 10, border: :thick)
|
|
201
240
|
```
|
|
202
241
|
|
|
203
242
|
and printing the box out to console will produce:
|
|
@@ -217,10 +256,66 @@ print box
|
|
|
217
256
|
# ╚════════════════════════════╝
|
|
218
257
|
```
|
|
219
258
|
|
|
220
|
-
You can also selectively
|
|
259
|
+
You can also selectively specify and turn off border parts by passing a hash with a `:border` key. The border parts are:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
:top
|
|
263
|
+
:top_left ┌────────┐ :top_right
|
|
264
|
+
│ │
|
|
265
|
+
:left │ │ :right
|
|
266
|
+
│ │
|
|
267
|
+
:bottom_left └────────┘ :bottom_right
|
|
268
|
+
:bottom
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The following are available border parts values:
|
|
272
|
+
|
|
273
|
+
| Border values | ASCII | Unicode Light | Unicode Thick |
|
|
274
|
+
| -------------------- |:-----:|:-------------:|:-------------:|
|
|
275
|
+
| :line | `-` | `─` | `═` |
|
|
276
|
+
| :pipe | `\|` | `\│` | `\║` |
|
|
277
|
+
| :cross | `+` | `┼` | `╬` |
|
|
278
|
+
| :divider_up | `+` | `┴` | `╩` |
|
|
279
|
+
| :divider_down | `+` | `┬` | `╦` |
|
|
280
|
+
| :divider_left | `+` | `┤` | `╣` |
|
|
281
|
+
| :divider_right | `+` | `├` | `╠` |
|
|
282
|
+
| :corner_top_left | `+` | `┌` | `╔` |
|
|
283
|
+
| :corner_top_right | `+` | `┐` | `╗` |
|
|
284
|
+
| :corner_bottom_left | `+` | `└` | `╚` |
|
|
285
|
+
| :corner_bottom_right | `+` | `┘` | `╝` |
|
|
286
|
+
|
|
287
|
+
For example, to change all box corners to be a `:cross` do:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
box = TTY::Box.frame(
|
|
291
|
+
width: 10, height: 4,
|
|
292
|
+
border: {
|
|
293
|
+
top_left: :cross,
|
|
294
|
+
top_right: :cross,
|
|
295
|
+
bottom_left: :cross,
|
|
296
|
+
bottom_right: :cross
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
```
|
|
221
300
|
|
|
222
301
|
```ruby
|
|
223
|
-
|
|
302
|
+
print box
|
|
303
|
+
# =>
|
|
304
|
+
# ┼────────┼
|
|
305
|
+
# │ │
|
|
306
|
+
# │ │
|
|
307
|
+
# ┼────────┼
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
If you want to remove a given border element as a value use `false`. For example to remove bottom border do:
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
TTY::Box.new(
|
|
314
|
+
width: 30, height: 10,
|
|
315
|
+
border: {
|
|
316
|
+
type: :thick,
|
|
317
|
+
bottom: false
|
|
318
|
+
})
|
|
224
319
|
```
|
|
225
320
|
|
|
226
321
|
### 2.6 styling
|
|
@@ -301,6 +396,90 @@ print box
|
|
|
301
396
|
#
|
|
302
397
|
```
|
|
303
398
|
|
|
399
|
+
### 2.8 messages
|
|
400
|
+
|
|
401
|
+

|
|
402
|
+
|
|
403
|
+
#### 2.8.1 info
|
|
404
|
+
|
|
405
|
+
To draw an information type box around your content use `info`:
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
box = TTY::Box.info("Deploying application")
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
And then print:
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
print box
|
|
415
|
+
# =>
|
|
416
|
+
# ╔ ℹ INFO ═══════════════╗
|
|
417
|
+
# ║ ║
|
|
418
|
+
# ║ Deploying application ║
|
|
419
|
+
# ║ ║
|
|
420
|
+
# ╚═══════════════════════╝
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
#### 2.8.2 warn
|
|
424
|
+
|
|
425
|
+
To draw a warning type box around your content use `warn`:
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
box = TTY::Box.warn("Deploying application")
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
And then print:
|
|
432
|
+
|
|
433
|
+
```ruby
|
|
434
|
+
print box
|
|
435
|
+
# =>
|
|
436
|
+
# ╔ ⚠ WARNING ════════════╗
|
|
437
|
+
# ║ ║
|
|
438
|
+
# ║ Deploying application ║
|
|
439
|
+
# ║ ║
|
|
440
|
+
# ╚═══════════════════════╝
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
#### 2.8.3 success
|
|
444
|
+
|
|
445
|
+
To draw a success type box around your content use `success`:
|
|
446
|
+
|
|
447
|
+
```ruby
|
|
448
|
+
box = TTY::Box.success("Deploying application")
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
And then print:
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
print box
|
|
455
|
+
# =>
|
|
456
|
+
# ╔ ✔ OK ═════════════════╗
|
|
457
|
+
# ║ ║
|
|
458
|
+
# ║ Deploying application ║
|
|
459
|
+
# ║ ║
|
|
460
|
+
# ╚═══════════════════════╝
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
#### 2.8.4 error
|
|
464
|
+
|
|
465
|
+
To draw an error type box around your content use `error`:
|
|
466
|
+
|
|
467
|
+
```ruby
|
|
468
|
+
box = TTY::Box.error("Deploying application")
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
And then print:
|
|
472
|
+
|
|
473
|
+
```ruby
|
|
474
|
+
print box
|
|
475
|
+
# =>
|
|
476
|
+
# ╔ ⨯ ERROR ══════════════╗
|
|
477
|
+
# ║ ║
|
|
478
|
+
# ║ Deploying application ║
|
|
479
|
+
# ║ ║
|
|
480
|
+
# ╚═══════════════════════╝
|
|
481
|
+
```
|
|
482
|
+
|
|
304
483
|
## Development
|
|
305
484
|
|
|
306
485
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/tty/box.rb
CHANGED
|
@@ -11,53 +11,58 @@ module TTY
|
|
|
11
11
|
module Box
|
|
12
12
|
module_function
|
|
13
13
|
|
|
14
|
+
NEWLINE = "\n"
|
|
15
|
+
|
|
16
|
+
LINE_BREAK = %r{\r\n|\r|\n}.freeze
|
|
17
|
+
|
|
14
18
|
BOX_CHARS = {
|
|
19
|
+
ascii: %w[+ + + + + + + + - | +],
|
|
15
20
|
light: %w[┘ ┐ ┌ └ ┤ ┴ ┬ ├ ─ │ ┼],
|
|
16
21
|
thick: %w[╝ ╗ ╔ ╚ ╣ ╩ ╦ ╠ ═ ║ ╬]
|
|
17
22
|
}.freeze
|
|
18
23
|
|
|
19
|
-
def
|
|
20
|
-
BOX_CHARS[border][
|
|
24
|
+
def corner_bottom_right_char(border = :light)
|
|
25
|
+
BOX_CHARS[border][0]
|
|
21
26
|
end
|
|
22
27
|
|
|
23
|
-
def
|
|
24
|
-
BOX_CHARS[border][
|
|
28
|
+
def corner_top_right_char(border = :light)
|
|
29
|
+
BOX_CHARS[border][1]
|
|
25
30
|
end
|
|
26
31
|
|
|
27
|
-
def
|
|
28
|
-
BOX_CHARS[border][
|
|
32
|
+
def corner_top_left_char(border = :light)
|
|
33
|
+
BOX_CHARS[border][2]
|
|
29
34
|
end
|
|
30
35
|
|
|
31
|
-
def
|
|
32
|
-
BOX_CHARS[border][
|
|
36
|
+
def corner_bottom_left_char(border = :light)
|
|
37
|
+
BOX_CHARS[border][3]
|
|
33
38
|
end
|
|
34
39
|
|
|
35
|
-
def
|
|
40
|
+
def divider_left_char(border = :light)
|
|
36
41
|
BOX_CHARS[border][4]
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
def
|
|
40
|
-
BOX_CHARS[border][
|
|
44
|
+
def divider_up_char(border = :light)
|
|
45
|
+
BOX_CHARS[border][5]
|
|
41
46
|
end
|
|
42
47
|
|
|
43
|
-
def
|
|
48
|
+
def divider_down_char(border = :light)
|
|
44
49
|
BOX_CHARS[border][6]
|
|
45
50
|
end
|
|
46
51
|
|
|
47
|
-
def
|
|
48
|
-
BOX_CHARS[border][
|
|
52
|
+
def divider_right_char(border = :light)
|
|
53
|
+
BOX_CHARS[border][7]
|
|
49
54
|
end
|
|
50
55
|
|
|
51
|
-
def
|
|
52
|
-
BOX_CHARS[border][
|
|
56
|
+
def line_char(border = :light)
|
|
57
|
+
BOX_CHARS[border][8]
|
|
53
58
|
end
|
|
54
59
|
|
|
55
|
-
def
|
|
56
|
-
BOX_CHARS[border][
|
|
60
|
+
def pipe_char(border = :light)
|
|
61
|
+
BOX_CHARS[border][9]
|
|
57
62
|
end
|
|
58
63
|
|
|
59
|
-
def
|
|
60
|
-
BOX_CHARS[border][
|
|
64
|
+
def cross_char(border = :light)
|
|
65
|
+
BOX_CHARS[border][10]
|
|
61
66
|
end
|
|
62
67
|
|
|
63
68
|
def cursor
|
|
@@ -68,70 +73,228 @@ module TTY
|
|
|
68
73
|
@color ||= Pastel.new
|
|
69
74
|
end
|
|
70
75
|
|
|
76
|
+
# A frame for info type message
|
|
77
|
+
#
|
|
78
|
+
# @param [String] message
|
|
79
|
+
# the message to display
|
|
80
|
+
#
|
|
81
|
+
# @api public
|
|
82
|
+
def info(message, **opts)
|
|
83
|
+
new_opts = {
|
|
84
|
+
title: { top_left: " ℹ INFO " },
|
|
85
|
+
border: { type: :thick },
|
|
86
|
+
padding: 1,
|
|
87
|
+
style: {
|
|
88
|
+
fg: :black,
|
|
89
|
+
bg: :bright_blue,
|
|
90
|
+
border: {
|
|
91
|
+
fg: :black,
|
|
92
|
+
bg: :bright_blue
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}.merge(opts)
|
|
96
|
+
frame(**new_opts) { message }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# A frame for warning type message
|
|
100
|
+
#
|
|
101
|
+
# @param [String] message
|
|
102
|
+
# the message to display
|
|
103
|
+
#
|
|
104
|
+
# @api public
|
|
105
|
+
def warn(message, **opts)
|
|
106
|
+
new_opts = {
|
|
107
|
+
title: { top_left: " ⚠ WARNING " },
|
|
108
|
+
border: { type: :thick },
|
|
109
|
+
padding: 1,
|
|
110
|
+
style: {
|
|
111
|
+
fg: :black,
|
|
112
|
+
bg: :bright_yellow,
|
|
113
|
+
border: {
|
|
114
|
+
fg: :black,
|
|
115
|
+
bg: :bright_yellow
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}.merge(opts)
|
|
119
|
+
frame(**new_opts) { message }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# A frame for for success type message
|
|
123
|
+
#
|
|
124
|
+
# @param [String] message
|
|
125
|
+
# the message to display
|
|
126
|
+
#
|
|
127
|
+
# @api public
|
|
128
|
+
def success(message, **opts)
|
|
129
|
+
new_opts = {
|
|
130
|
+
title: { top_left: " ✔ OK " },
|
|
131
|
+
border: { type: :thick },
|
|
132
|
+
padding: 1,
|
|
133
|
+
style: {
|
|
134
|
+
fg: :black,
|
|
135
|
+
bg: :bright_green,
|
|
136
|
+
border: {
|
|
137
|
+
fg: :black,
|
|
138
|
+
bg: :bright_green,
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}.merge(opts)
|
|
142
|
+
frame(**new_opts) { message }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# A frame for error type message
|
|
146
|
+
#
|
|
147
|
+
# @param [String] message
|
|
148
|
+
# the message to display
|
|
149
|
+
#
|
|
150
|
+
# @api public
|
|
151
|
+
def error(message, **opts)
|
|
152
|
+
new_opts = {
|
|
153
|
+
title: { top_left: " ⨯ ERROR " },
|
|
154
|
+
border: { type: :thick },
|
|
155
|
+
padding: 1,
|
|
156
|
+
style: {
|
|
157
|
+
fg: :bright_white,
|
|
158
|
+
bg: :red,
|
|
159
|
+
border: {
|
|
160
|
+
fg: :bright_white,
|
|
161
|
+
bg: :red
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}.merge(opts)
|
|
165
|
+
frame(**new_opts) { message }
|
|
166
|
+
end
|
|
167
|
+
|
|
71
168
|
# Create a frame
|
|
72
169
|
#
|
|
170
|
+
# @param [Integer] top
|
|
171
|
+
# the offset from the terminal top
|
|
172
|
+
# @param [Integer] left
|
|
173
|
+
# the offset from the terminal left
|
|
174
|
+
# @param [Integer] width
|
|
175
|
+
# the width of the box
|
|
176
|
+
# @param [Integer] height
|
|
177
|
+
# the height of the box
|
|
178
|
+
# @param [Symbol] align
|
|
179
|
+
# the content alignment
|
|
180
|
+
# @param [Integer,Array[Integer]] padding
|
|
181
|
+
# the padding around content
|
|
182
|
+
# @param [Hash] title
|
|
183
|
+
# the title for top or bottom border
|
|
184
|
+
# @param [Hash, Symbol] border
|
|
185
|
+
# the border type
|
|
186
|
+
# @param [Hash] style
|
|
187
|
+
# the styling for the front and background
|
|
188
|
+
#
|
|
73
189
|
# @api public
|
|
74
|
-
def frame(top: nil, left: nil, width:
|
|
75
|
-
title: {}, border: :light, style: {})
|
|
190
|
+
def frame(*content, top: nil, left: nil, width: nil, height: nil, align: :left,
|
|
191
|
+
padding: 0, title: {}, border: :light, style: {})
|
|
76
192
|
output = []
|
|
77
|
-
|
|
193
|
+
sep = NEWLINE
|
|
78
194
|
position = top && left
|
|
79
195
|
|
|
80
196
|
border = Border.parse(border)
|
|
197
|
+
top_size = border.top? ? 1: 0
|
|
198
|
+
bottom_size = border.bottom? ? 1: 0
|
|
199
|
+
left_size = border.left? ? 1 : 0
|
|
200
|
+
right_size = border.right ? 1 : 0
|
|
81
201
|
|
|
82
202
|
if block_given?
|
|
83
|
-
|
|
203
|
+
str = yield
|
|
204
|
+
else
|
|
205
|
+
str = case content.size
|
|
206
|
+
when 0 then ""
|
|
207
|
+
when 1 then content[0]
|
|
208
|
+
else content.join(NEWLINE)
|
|
209
|
+
end
|
|
84
210
|
end
|
|
85
211
|
|
|
212
|
+
sep = str[LINE_BREAK] || NEWLINE # infer line break
|
|
213
|
+
lines = str.split(sep)
|
|
214
|
+
# infer dimensions
|
|
215
|
+
dimensions = infer_dimensions(lines, padding)
|
|
216
|
+
width ||= left_size + dimensions[0] + right_size
|
|
217
|
+
width = [width, top_space_taken(title, border), bottom_space_taken(title, border)].max
|
|
218
|
+
|
|
219
|
+
height ||= top_size + dimensions[1] + bottom_size
|
|
220
|
+
content = format(str, width, padding, align) # adjust content
|
|
221
|
+
# infer styling
|
|
86
222
|
fg, bg = *extract_style(style)
|
|
87
223
|
border_fg, border_bg = *extract_style(style[:border] || {})
|
|
88
224
|
|
|
89
225
|
if border.top?
|
|
90
226
|
output << cursor.move_to(left, top) if position
|
|
91
|
-
output << top_border(title, width, border
|
|
92
|
-
output <<
|
|
227
|
+
output << top_border(title, width, border, style)
|
|
228
|
+
output << sep unless position
|
|
93
229
|
end
|
|
94
|
-
|
|
230
|
+
|
|
231
|
+
(height - top_size - bottom_size).times do |i|
|
|
232
|
+
output << cursor.move_to(left, top + i + top_size) if position
|
|
95
233
|
if border.left?
|
|
96
|
-
output << cursor.move_to(left, top + i + 1) if position
|
|
97
234
|
output << border_bg.(border_fg.(pipe_char(border.type)))
|
|
98
235
|
end
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
236
|
+
|
|
237
|
+
content_size = width - left_size - right_size
|
|
238
|
+
unless content[i].nil?
|
|
102
239
|
output << bg.(fg.(content[i]))
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
240
|
+
size = Strings::ANSI.sanitize(content[i]).scan(/[[:print:]]/).join.size
|
|
241
|
+
content_size -= size
|
|
242
|
+
end
|
|
243
|
+
if style[:fg] || style[:bg] || !position # something to color
|
|
244
|
+
output << bg.(fg.(" " * content_size))
|
|
106
245
|
end
|
|
246
|
+
|
|
107
247
|
if border.right?
|
|
108
|
-
|
|
248
|
+
if position
|
|
249
|
+
output << cursor.move_to(left + width - right_size, top + i + top_size)
|
|
250
|
+
end
|
|
109
251
|
output << border_bg.(border_fg.(pipe_char(border.type)))
|
|
110
252
|
end
|
|
111
|
-
output <<
|
|
253
|
+
output << sep unless position
|
|
112
254
|
end
|
|
255
|
+
|
|
113
256
|
if border.bottom?
|
|
114
|
-
output << cursor.move_to(left, top + height -
|
|
115
|
-
output << bottom_border(title, width, border
|
|
116
|
-
output <<
|
|
257
|
+
output << cursor.move_to(left, top + height - bottom_size) if position
|
|
258
|
+
output << bottom_border(title, width, border, style)
|
|
259
|
+
output << sep unless position
|
|
117
260
|
end
|
|
118
261
|
|
|
119
262
|
output.join
|
|
120
263
|
end
|
|
121
264
|
|
|
265
|
+
# Infer box dimensions based on content lines and padding
|
|
266
|
+
#
|
|
267
|
+
# @param [Array[String]] lines
|
|
268
|
+
# @param [Array[Integer]|Integer] padding
|
|
269
|
+
#
|
|
270
|
+
# @return [Array[Integer]]
|
|
271
|
+
#
|
|
272
|
+
# @api private
|
|
273
|
+
def infer_dimensions(lines, padding)
|
|
274
|
+
pad = Strings::Padder.parse(padding)
|
|
275
|
+
content_height = lines.size
|
|
276
|
+
content_width = lines.empty? ? 1 : lines.max_by(&:length).length
|
|
277
|
+
width = pad.left + content_width + pad.right
|
|
278
|
+
height = pad.top + content_height + pad.bottom
|
|
279
|
+
[width, height]
|
|
280
|
+
end
|
|
281
|
+
|
|
122
282
|
# Format content
|
|
123
283
|
#
|
|
124
284
|
# @return [Array[String]]
|
|
125
285
|
#
|
|
126
286
|
# @api private
|
|
127
287
|
def format(content, width, padding, align)
|
|
288
|
+
return "" if content.to_s.empty?
|
|
289
|
+
|
|
128
290
|
pad = Strings::Padder.parse(padding)
|
|
129
291
|
total_width = width - 2 - (pad.left + pad.right)
|
|
292
|
+
sep = content[LINE_BREAK] || NEWLINE # infer line break
|
|
130
293
|
|
|
131
294
|
wrapped = Strings.wrap(content, total_width)
|
|
132
295
|
aligned = Strings.align(wrapped, total_width, direction: align)
|
|
133
296
|
padded = Strings.pad(aligned, padding)
|
|
134
|
-
padded.split(
|
|
297
|
+
padded.split(sep)
|
|
135
298
|
end
|
|
136
299
|
|
|
137
300
|
# Convert style keywords into styling
|
|
@@ -145,32 +308,99 @@ module TTY
|
|
|
145
308
|
[fg, bg]
|
|
146
309
|
end
|
|
147
310
|
|
|
311
|
+
# Top space taken by titles and corners
|
|
312
|
+
#
|
|
313
|
+
# @return [Integer]
|
|
314
|
+
#
|
|
315
|
+
# @api private
|
|
316
|
+
def top_space_taken(title, border)
|
|
317
|
+
top_titles_size(title) + top_left_corner(border).size + top_right_corner(border).size
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Top left corner
|
|
321
|
+
#
|
|
322
|
+
# @return [String]
|
|
323
|
+
#
|
|
324
|
+
# @api private
|
|
325
|
+
def top_left_corner(border)
|
|
326
|
+
border.top_left? && border.left? ? send(:"#{border.top_left}_char", border.type) : ""
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Top right corner
|
|
330
|
+
#
|
|
331
|
+
# @return [String]
|
|
332
|
+
#
|
|
333
|
+
# @api private
|
|
334
|
+
def top_right_corner(border)
|
|
335
|
+
border.top_right? && border.right? ? send(:"#{border.top_right}_char", border.type) : ""
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Top titles size
|
|
339
|
+
#
|
|
340
|
+
# @return [Integer]
|
|
341
|
+
#
|
|
342
|
+
# @api private
|
|
343
|
+
def top_titles_size(title)
|
|
344
|
+
title[:top_left].to_s.size + title[:top_center].to_s.size + title[:top_right].to_s.size
|
|
345
|
+
end
|
|
346
|
+
|
|
148
347
|
# Top border
|
|
149
348
|
#
|
|
150
349
|
# @return [String]
|
|
151
350
|
#
|
|
152
351
|
# @api private
|
|
153
352
|
def top_border(title, width, border, style)
|
|
154
|
-
top_titles_size = title[:top_left].to_s.size +
|
|
155
|
-
title[:top_center].to_s.size +
|
|
156
|
-
title[:top_right].to_s.size
|
|
157
353
|
fg, bg = *extract_style(style[:border] || {})
|
|
158
354
|
|
|
159
|
-
top_space_left = width -
|
|
160
|
-
top_left_char.size - top_right_char.size
|
|
355
|
+
top_space_left = width - top_space_taken(title, border)
|
|
161
356
|
top_space_before = top_space_left / 2
|
|
162
|
-
top_space_after
|
|
357
|
+
top_space_after = top_space_left / 2 + top_space_left % 2
|
|
163
358
|
|
|
164
359
|
[
|
|
165
|
-
bg.(fg.(
|
|
166
|
-
bg.(title[:top_left].to_s),
|
|
167
|
-
bg.(fg.(line_char(border) * top_space_before)),
|
|
168
|
-
bg.(title[:top_center].to_s),
|
|
169
|
-
bg.(fg.(line_char(border) * top_space_after)),
|
|
170
|
-
bg.(title[:top_right].to_s),
|
|
171
|
-
bg.(fg.(
|
|
360
|
+
bg.(fg.(top_left_corner(border))),
|
|
361
|
+
bg.(fg.(title[:top_left].to_s)),
|
|
362
|
+
bg.(fg.(line_char(border.type) * top_space_before)),
|
|
363
|
+
bg.(fg.(title[:top_center].to_s)),
|
|
364
|
+
bg.(fg.(line_char(border.type) * top_space_after)),
|
|
365
|
+
bg.(fg.(title[:top_right].to_s)),
|
|
366
|
+
bg.(fg.(top_right_corner(border)))
|
|
172
367
|
].join('')
|
|
173
368
|
end
|
|
369
|
+
# Bottom space taken by titles and corners
|
|
370
|
+
#
|
|
371
|
+
# @return [Integer]
|
|
372
|
+
#
|
|
373
|
+
# @api private
|
|
374
|
+
def bottom_space_taken(title, border)
|
|
375
|
+
bottom_titles_size(title) + bottom_left_corner(border).size + bottom_right_corner(border).size
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Bottom left corner
|
|
379
|
+
#
|
|
380
|
+
# @return [String]
|
|
381
|
+
#
|
|
382
|
+
# @api private
|
|
383
|
+
def bottom_left_corner(border)
|
|
384
|
+
border.bottom_left? && border.left? ? send(:"#{border.bottom_left}_char", border.type) : ""
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Bottom right corner
|
|
388
|
+
#
|
|
389
|
+
# @return [String]
|
|
390
|
+
#
|
|
391
|
+
# @api private
|
|
392
|
+
def bottom_right_corner(border)
|
|
393
|
+
border.bottom_right? && border.right? ? send(:"#{border.bottom_right}_char", border.type) : ""
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Bottom titles size
|
|
397
|
+
#
|
|
398
|
+
# @return [Integer]
|
|
399
|
+
#
|
|
400
|
+
# @api private
|
|
401
|
+
def bottom_titles_size(title)
|
|
402
|
+
title[:bottom_left].to_s.size + title[:bottom_center].to_s.size + title[:bottom_right].to_s.size
|
|
403
|
+
end
|
|
174
404
|
|
|
175
405
|
# Bottom border
|
|
176
406
|
#
|
|
@@ -178,24 +408,20 @@ module TTY
|
|
|
178
408
|
#
|
|
179
409
|
# @api private
|
|
180
410
|
def bottom_border(title, width, border, style)
|
|
181
|
-
bottom_titles_size = title[:bottom_left].to_s.size +
|
|
182
|
-
title[:bottom_center].to_s.size +
|
|
183
|
-
title[:bottom_right].to_s.size
|
|
184
411
|
fg, bg = *extract_style(style[:border] || {})
|
|
185
412
|
|
|
186
|
-
bottom_space_left = width -
|
|
187
|
-
bottom_left_char.size - bottom_right_char.size
|
|
413
|
+
bottom_space_left = width - bottom_space_taken(title, border)
|
|
188
414
|
bottom_space_before = bottom_space_left / 2
|
|
189
415
|
bottom_space_after = bottom_space_left / 2 + bottom_space_left % 2
|
|
190
416
|
|
|
191
417
|
[
|
|
192
|
-
bg.(fg.(
|
|
193
|
-
bg.(title[:bottom_left].to_s),
|
|
194
|
-
bg.(fg.(line_char(border) * bottom_space_before)),
|
|
195
|
-
bg.(title[:bottom_center].to_s),
|
|
196
|
-
bg.(fg.(line_char(border) * bottom_space_after)),
|
|
197
|
-
bg.(title[:bottom_right].to_s),
|
|
198
|
-
bg.(fg.(
|
|
418
|
+
bg.(fg.(bottom_left_corner(border))),
|
|
419
|
+
bg.(fg.(title[:bottom_left].to_s)),
|
|
420
|
+
bg.(fg.(line_char(border.type) * bottom_space_before)),
|
|
421
|
+
bg.(fg.(title[:bottom_center].to_s)),
|
|
422
|
+
bg.(fg.(line_char(border.type) * bottom_space_after)),
|
|
423
|
+
bg.(fg.(title[:bottom_right].to_s)),
|
|
424
|
+
bg.(fg.(bottom_right_corner(border)))
|
|
199
425
|
].join('')
|
|
200
426
|
end
|
|
201
427
|
end # TTY
|