rich-ruby 1.0.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.
data/lib/rich/box.rb ADDED
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rich
4
+ # Box drawing character sets for borders and tables
5
+ class Box
6
+ # @return [String] Top-left corner
7
+ attr_reader :top_left
8
+
9
+ # @return [String] Top-right corner
10
+ attr_reader :top_right
11
+
12
+ # @return [String] Bottom-left corner
13
+ attr_reader :bottom_left
14
+
15
+ # @return [String] Bottom-right corner
16
+ attr_reader :bottom_right
17
+
18
+ # @return [String] Horizontal line
19
+ attr_reader :horizontal
20
+
21
+ # @return [String] Vertical line
22
+ attr_reader :vertical
23
+
24
+ # @return [String] Left T-junction
25
+ attr_reader :left_t
26
+
27
+ # @return [String] Right T-junction
28
+ attr_reader :right_t
29
+
30
+ # @return [String] Top T-junction
31
+ attr_reader :top_t
32
+
33
+ # @return [String] Bottom T-junction
34
+ attr_reader :bottom_t
35
+
36
+ # @return [String] Cross/plus junction
37
+ attr_reader :cross
38
+
39
+ # @return [String] Thick horizontal (for headers)
40
+ attr_reader :thick_horizontal
41
+
42
+ # @return [String] Thick left T-junction
43
+ attr_reader :thick_left_t
44
+
45
+ # @return [String] Thick right T-junction
46
+ attr_reader :thick_right_t
47
+
48
+ # @return [String] Thick cross
49
+ attr_reader :thick_cross
50
+
51
+ def initialize(
52
+ top_left:,
53
+ top_right:,
54
+ bottom_left:,
55
+ bottom_right:,
56
+ horizontal:,
57
+ vertical:,
58
+ left_t: nil,
59
+ right_t: nil,
60
+ top_t: nil,
61
+ bottom_t: nil,
62
+ cross: nil,
63
+ thick_horizontal: nil,
64
+ thick_left_t: nil,
65
+ thick_right_t: nil,
66
+ thick_cross: nil
67
+ )
68
+ @top_left = top_left
69
+ @top_right = top_right
70
+ @bottom_left = bottom_left
71
+ @bottom_right = bottom_right
72
+ @horizontal = horizontal
73
+ @vertical = vertical
74
+ @left_t = left_t || vertical
75
+ @right_t = right_t || vertical
76
+ @top_t = top_t || horizontal
77
+ @bottom_t = bottom_t || horizontal
78
+ @cross = cross || "+"
79
+ @thick_horizontal = thick_horizontal || horizontal
80
+ @thick_left_t = thick_left_t || @left_t
81
+ @thick_right_t = thick_right_t || @right_t
82
+ @thick_cross = thick_cross || @cross
83
+ freeze
84
+ end
85
+
86
+ # Get the top edge
87
+ # @param width [Integer] Width of content
88
+ # @return [String]
89
+ def top_edge(width)
90
+ "#{@top_left}#{@horizontal * [0, width - 2].max}#{@top_right}"
91
+ end
92
+
93
+ # Get the bottom edge
94
+ # @param width [Integer] Width of content
95
+ # @return [String]
96
+ def bottom_edge(width)
97
+ "#{@bottom_left}#{@horizontal * [0, width - 2].max}#{@bottom_right}"
98
+ end
99
+
100
+ # Get the row separator
101
+ # @param width_or_cells [Integer, Array] Total width or array of cell contents
102
+ # @param widths [Array<Integer>, nil] Array of column widths
103
+ # @return [String]
104
+ def row(width_or_cells, widths = nil)
105
+ if widths
106
+ # Table row separator with multiple columns
107
+ parts = widths.map { |w| @horizontal * w }
108
+ "#{@left_t}#{parts.join(@cross)}#{@right_t}"
109
+ else
110
+ # Single column separator
111
+ width = width_or_cells.is_a?(Integer) ? width_or_cells : Cells.cell_len(width_or_cells.to_s)
112
+ "#{@left_t}#{@horizontal * [0, width - 2].max}#{@right_t}"
113
+ end
114
+ end
115
+
116
+ alias top top_edge
117
+ alias bottom bottom_edge
118
+
119
+ # Get a content row
120
+ # @param content [String] Content
121
+ # @param width [Integer] Width to pad to
122
+ # @param align [Symbol] Alignment (:left, :center, :right)
123
+ # @return [String]
124
+ def content_row(content, width, align: :left)
125
+ content_len = Cells.cell_len(content)
126
+ padding = width - content_len
127
+
128
+ case align
129
+ when :center
130
+ left_pad = padding / 2
131
+ right_pad = padding - left_pad
132
+ "#{@vertical}#{' ' * left_pad}#{content}#{' ' * right_pad}#{@vertical}"
133
+ when :right
134
+ "#{@vertical}#{' ' * padding}#{content}#{@vertical}"
135
+ else # :left
136
+ "#{@vertical}#{content}#{' ' * padding}#{@vertical}"
137
+ end
138
+ end
139
+
140
+ # Get header separator (thicker line)
141
+ # @param width [Integer] Width
142
+ # @return [String]
143
+ def header_separator(width)
144
+ "#{@thick_left_t}#{@thick_horizontal * width}#{@thick_right_t}"
145
+ end
146
+
147
+ # Substitute ASCII characters for box characters
148
+ # @return [Box]
149
+ def to_ascii
150
+ ASCII
151
+ end
152
+
153
+ # Check if this is the ASCII box
154
+ # @return [Boolean]
155
+ def ascii?
156
+ self == ASCII
157
+ end
158
+
159
+ # Predefined box styles
160
+ class << self
161
+ # ASCII characters only
162
+ def ascii
163
+ ASCII
164
+ end
165
+
166
+ # Standard Unicode box drawing
167
+ def square
168
+ SQUARE
169
+ end
170
+
171
+ # Rounded corners
172
+ def rounded
173
+ ROUNDED
174
+ end
175
+
176
+ # Heavy/thick lines
177
+ def heavy
178
+ HEAVY
179
+ end
180
+
181
+ # Double lines
182
+ def double
183
+ DOUBLE
184
+ end
185
+
186
+ # Minimal (no corners)
187
+ def minimal
188
+ MINIMAL
189
+ end
190
+
191
+ # Simple horizontal lines only
192
+ def simple
193
+ SIMPLE
194
+ end
195
+
196
+ # No border
197
+ def none
198
+ NONE
199
+ end
200
+ end
201
+
202
+ # ASCII box (works everywhere)
203
+ ASCII = new(
204
+ top_left: "+",
205
+ top_right: "+",
206
+ bottom_left: "+",
207
+ bottom_right: "+",
208
+ horizontal: "-",
209
+ vertical: "|",
210
+ left_t: "+",
211
+ right_t: "+",
212
+ top_t: "+",
213
+ bottom_t: "+",
214
+ cross: "+",
215
+ thick_horizontal: "=",
216
+ thick_left_t: "+",
217
+ thick_right_t: "+",
218
+ thick_cross: "+"
219
+ )
220
+
221
+ # Standard Unicode box
222
+ SQUARE = new(
223
+ top_left: "┌",
224
+ top_right: "┐",
225
+ bottom_left: "└",
226
+ bottom_right: "┘",
227
+ horizontal: "─",
228
+ vertical: "│",
229
+ left_t: "├",
230
+ right_t: "┤",
231
+ top_t: "┬",
232
+ bottom_t: "┴",
233
+ cross: "┼",
234
+ thick_horizontal: "━",
235
+ thick_left_t: "┝",
236
+ thick_right_t: "┥",
237
+ thick_cross: "┿"
238
+ )
239
+
240
+ # Rounded corners
241
+ ROUNDED = new(
242
+ top_left: "╭",
243
+ top_right: "╮",
244
+ bottom_left: "╰",
245
+ bottom_right: "╯",
246
+ horizontal: "─",
247
+ vertical: "│",
248
+ left_t: "├",
249
+ right_t: "┤",
250
+ top_t: "┬",
251
+ bottom_t: "┴",
252
+ cross: "┼",
253
+ thick_horizontal: "━",
254
+ thick_left_t: "┝",
255
+ thick_right_t: "┥",
256
+ thick_cross: "┿"
257
+ )
258
+
259
+ # Heavy/thick box
260
+ HEAVY = new(
261
+ top_left: "┏",
262
+ top_right: "┓",
263
+ bottom_left: "┗",
264
+ bottom_right: "┛",
265
+ horizontal: "━",
266
+ vertical: "┃",
267
+ left_t: "┣",
268
+ right_t: "┫",
269
+ top_t: "┳",
270
+ bottom_t: "┻",
271
+ cross: "╋",
272
+ thick_horizontal: "━",
273
+ thick_left_t: "┣",
274
+ thick_right_t: "┫",
275
+ thick_cross: "╋"
276
+ )
277
+
278
+ # Double line box
279
+ DOUBLE = new(
280
+ top_left: "╔",
281
+ top_right: "╗",
282
+ bottom_left: "╚",
283
+ bottom_right: "╝",
284
+ horizontal: "═",
285
+ vertical: "║",
286
+ left_t: "╠",
287
+ right_t: "╣",
288
+ top_t: "╦",
289
+ bottom_t: "╩",
290
+ cross: "╬",
291
+ thick_horizontal: "═",
292
+ thick_left_t: "╠",
293
+ thick_right_t: "╣",
294
+ thick_cross: "╬"
295
+ )
296
+
297
+ # Minimal (dashes, no corners)
298
+ MINIMAL = new(
299
+ top_left: " ",
300
+ top_right: " ",
301
+ bottom_left: " ",
302
+ bottom_right: " ",
303
+ horizontal: "─",
304
+ vertical: " ",
305
+ left_t: " ",
306
+ right_t: " ",
307
+ top_t: "─",
308
+ bottom_t: "─",
309
+ cross: "─"
310
+ )
311
+
312
+ # Simple (just horizontal lines)
313
+ SIMPLE = new(
314
+ top_left: "",
315
+ top_right: "",
316
+ bottom_left: "",
317
+ bottom_right: "",
318
+ horizontal: "─",
319
+ vertical: "",
320
+ left_t: "",
321
+ right_t: "",
322
+ top_t: "",
323
+ bottom_t: "",
324
+ cross: ""
325
+ )
326
+
327
+ # No border at all
328
+ NONE = new(
329
+ top_left: "",
330
+ top_right: "",
331
+ bottom_left: "",
332
+ bottom_right: "",
333
+ horizontal: "",
334
+ vertical: "",
335
+ left_t: "",
336
+ right_t: "",
337
+ top_t: "",
338
+ bottom_t: "",
339
+ cross: ""
340
+ )
341
+ end
342
+ end