Skip to content

Commit 49c2975

Browse files
committed
feat: add VerticalBarStyle border (only vertical | separators, no outer box)
- Introduce VerticalBarStyle for tables with only vertical bar separators and no outer borders - Update border configuration and style selection logic - Add usage example for AutoAlign(false) with VerticalBarStyle - Minor code and doc improvements based on
1 parent ec81b3b commit 49c2975

File tree

7 files changed

+73
-20
lines changed

7 files changed

+73
-20
lines changed

borders.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const (
2626
DoubleStyle BorderStyle = "double"
2727
// MinimalStyle uses minimal borders.
2828
MinimalStyle BorderStyle = "minimal"
29+
// VerticalBarStyle uses only vertical bar separators (|), no outer borders.
30+
VerticalBarStyle BorderStyle = "vertical_bar"
2931
// MarkdownStyle uses Markdown table format.
3032
MarkdownStyle BorderStyle = "markdown"
3133
// TSVStyle uses tab separators only.
@@ -126,6 +128,29 @@ var (
126128
Padding: true,
127129
}
128130

131+
verticalBarConfig = TableBorderConfig{
132+
Chars: map[string]string{
133+
"horizontal": "",
134+
"vertical": "|",
135+
"cross": "|",
136+
"top_left": "",
137+
"top_right": "",
138+
"bottom_left": "",
139+
"bottom_right": "",
140+
"top_cross": "",
141+
"bottom_cross": "",
142+
"left_cross": "",
143+
"right_cross": "",
144+
},
145+
Top: false,
146+
Bottom: false,
147+
Middle: false,
148+
Left: false,
149+
Right: false,
150+
Vertical: true,
151+
Padding: true,
152+
}
153+
129154
minimalConfig = TableBorderConfig{
130155
Chars: map[string]string{
131156
"horizontal": " ",
@@ -207,6 +232,8 @@ func GetBorderConfig(style BorderStyle) TableBorderConfig {
207232
return doubleConfig
208233
case MinimalStyle:
209234
return minimalConfig
235+
case VerticalBarStyle:
236+
return verticalBarConfig
210237
case MarkdownStyle:
211238
return markdownConfig
212239
case TSVStyle:

examples/autoalign_false/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
"github.com/noborus/termhyo"
7+
)
8+
9+
func main() {
10+
columns := []termhyo.Column{
11+
{Title: "ID", Width: 0, Align: termhyo.Right},
12+
{Title: "Name", Width: 0, Align: termhyo.Left},
13+
{Title: "Score", Width: 0, Align: termhyo.Center},
14+
}
15+
16+
table := termhyo.NewTable(os.Stdout, columns, termhyo.Border(termhyo.VerticalBarStyle), termhyo.AutoAlign(false))
17+
table.AddRow("1", "Alice", "85")
18+
table.AddRow("2", "Bob", "92")
19+
table.AddRow("3", "Charlie", "78")
20+
table.Render()
21+
}

features_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func TestRenderModes(t *testing.T) {
192192
}
193193

194194
table := NewTable(&buf, columns)
195-
table.SetAlign(false) // This should trigger streaming mode
195+
table.SetAutoAlign(false) // This should trigger streaming mode
196196
table.AddRow("Data")
197197
table.Render()
198198

markdown.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type MarkdownRenderer struct {
1313

1414
// hasAutoWidth checks if any columns have auto width.
1515
func hasAutoWidth(table *Table) bool {
16-
if !table.align {
16+
if !table.autoAlign {
1717
return false // No auto width in streaming mode
1818
}
1919
for _, col := range table.columns {
@@ -93,7 +93,7 @@ func (r *MarkdownRenderer) renderMarkdownHeader(table *Table) error {
9393
for _, col := range table.columns {
9494
// Apply alignment to header content (headers are typically centered)
9595
content := col.Title
96-
if table.align {
96+
if table.autoAlign {
9797
content = table.formatCell(col.Title, col.Width, Center)
9898
}
9999
line += content + "|"
@@ -137,7 +137,7 @@ func (r *MarkdownRenderer) renderMarkdownRow(table *Table, row Row) error {
137137
}
138138

139139
for i, col := range table.columns {
140-
if !table.align {
140+
if !table.autoAlign {
141141
line += cells[i].Content + "|"
142142
continue // Skip alignment if noAlign is set
143143
}

table.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type Table struct {
1717
renderer Renderer
1818
borderStyle BorderStyle
1919
borderConfig TableBorderConfig
20-
align bool // If false, skip alignment for all columns
20+
autoAlign bool // If false, skip alignment for all columns
2121
borders map[string]string
2222
padding int
2323
headerStyle HeaderStyle // styling for header row
@@ -28,9 +28,9 @@ func (t *Table) GetBorderConfig() TableBorderConfig {
2828
return t.borderConfig
2929
}
3030

31-
// SetAlign sets whether to skip alignment for all columns.
32-
func (t *Table) SetAlign(align bool) {
33-
t.align = align
31+
// SetAutoAlign sets whether to skip alignment for all columns.
32+
func (t *Table) SetAutoAlign(autoAlign bool) {
33+
t.autoAlign = autoAlign
3434
// Recalculate render mode when alignment setting changes
3535
t.mode = t.determineRenderMode()
3636

@@ -46,7 +46,7 @@ func (t *Table) SetAlign(align bool) {
4646

4747
// GetAlign returns the current align setting.
4848
func (t *Table) GetAlign() bool {
49-
return t.align
49+
return t.autoAlign
5050
}
5151

5252
// NewTable creates a new table with the given columns and optional configuration.
@@ -70,7 +70,7 @@ func NewTable(writer io.Writer, columns []Column, opts ...TableOption) *Table {
7070
writer: writer,
7171
rows: make([]Row, 0),
7272
padding: 1,
73-
align: true, // Default to aligned columns
73+
autoAlign: true, // Default to auto-aligning columns
7474
borderStyle: BoxDrawingStyle,
7575
borderConfig: borderConfig,
7676
borders: borderConfig.Chars,
@@ -113,10 +113,10 @@ func Header(style HeaderStyle) TableOption {
113113
}
114114
}
115115

116-
// Align sets the align flag (option).
117-
func Align(align bool) TableOption {
116+
// AutoAlign sets the align flag (option).
117+
func AutoAlign(autoAlign bool) TableOption {
118118
return func(t *Table) {
119-
t.align = align
119+
t.autoAlign = autoAlign
120120
}
121121
}
122122

@@ -146,7 +146,7 @@ func (t *Table) determineRenderMode() RenderMode {
146146
}
147147

148148
// Use streaming mode if no auto-width calculation needed OR no alignment needed
149-
if !hasAutoWidth || !t.align {
149+
if !hasAutoWidth || !t.autoAlign {
150150
return StreamingMode
151151
}
152152

@@ -280,15 +280,15 @@ func (t *Table) RenderHeaderRow(row Row) error {
280280
content = cell.Content
281281

282282
// Apply alignment if not disabled
283-
if t.align {
283+
if t.autoAlign {
284284
align := col.Align
285285
if cell.Align != Default {
286286
align = cell.Align
287287
}
288288
content = t.formatCell(content, col.Width, align)
289289
}
290290
} else {
291-
if t.align {
291+
if t.autoAlign {
292292
if !t.borderConfig.Padding {
293293
// No padding for empty cells
294294
content = strings.Repeat(" ", col.Width)
@@ -341,15 +341,15 @@ func (t *Table) RenderRow(row Row) error {
341341
content = cell.Content
342342

343343
// Apply alignment if not disabled
344-
if t.align {
344+
if t.autoAlign {
345345
align := col.Align
346346
if cell.Align != Default {
347347
align = cell.Align
348348
}
349349
content = t.formatCell(content, col.Width, align)
350350
}
351351
} else {
352-
if t.align {
352+
if t.autoAlign {
353353
if !t.borderConfig.Padding {
354354
// No padding for empty cells
355355
content = strings.Repeat(" ", col.Width)

table_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ func testNoAlignMode() string {
413413
}
414414

415415
table := NewTable(&buf, columns)
416-
table.SetAlign(false) // Disable alignment
416+
table.SetAutoAlign(false) // Disable alignment
417417
table.AddRow("A", "B", "C")
418418
table.AddRow("LongText", "X", "Y")
419419
table.Render()

width.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func truncateWithEscapes(s string, maxWidth int) string {
103103
escapeStart := i
104104
i += 2 // Skip \x1b[
105105

106-
for i < len(runes) && !((runes[i] >= 'a' && runes[i] <= 'z') || (runes[i] >= 'A' && runes[i] <= 'Z')) {
106+
for i < len(runes) && !isAlpha(runes[i]) {
107107
i++
108108
}
109109
if i < len(runes) {
@@ -151,6 +151,11 @@ func truncateWithEscapes(s string, maxWidth int) string {
151151
return result.String()
152152
}
153153

154+
// isAlpha returns true if the rune is an ASCII alphabetic character.
155+
func isAlpha(r rune) bool {
156+
return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
157+
}
158+
154159
// padString pads a string to the specified display width with spaces.
155160
// Correctly handles ANSI escape sequences when calculating padding.
156161
func padString(s string, width int, align Alignment) string {

0 commit comments

Comments
 (0)