Class: Charming::Presentation::Components::Table

Inherits:
Charming::Presentation::Component show all
Includes:
KeyboardHandler
Defined in:
lib/charming/presentation/components/table.rb

Overview

Table renders tabular data with a header row, a selected row highlight, and keyboard navigation. Mouse clicks within the body area also select rows. The table is rendered via tty-table and the selected row is overlaid with reverse-video ANSI styling.

Constant Summary collapse

KEY_ACTIONS =

Maps navigation keys to the instance methods that move the selection. Shared with List and Viewport via KeyboardHandler.

{
  up: :move_up,
  down: :move_down,
  home: :move_home,
  end: :move_end
}.freeze
HEADER_HEIGHT =

Number of terminal rows occupied by the table’s top border and header line. Used by the mouse handler to translate absolute row coordinates to body rows.

2

Constants included from KeyboardHandler

KeyboardHandler::VIM_KEYMAP

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from View

#focused?, #layout_assigns

Constructor Details

#initialize(header:, rows: [], selected_index: 0, keymap: :vim) ⇒ Table

header is an array of column labels. rows is the array of body rows (each either a String, an Array, or a Hash of column-value pairs). selected_index defaults to 0. keymap selects the keybinding style (‘:vim` enables h/j/k/l → left/down/up/right).



33
34
35
36
37
38
39
# File 'lib/charming/presentation/components/table.rb', line 33

def initialize(header:, rows: [], selected_index: 0, keymap: :vim)
  super()
  @header = Array(header).map(&:to_s)
  @rows = Array(rows)
  @selected_index = clamp_index(selected_index)
  @keymap = keymap
end

Instance Attribute Details

#headerObject (readonly)

The header row, the body rows, and the currently selected row index, respectively.



28
29
30
# File 'lib/charming/presentation/components/table.rb', line 28

def header
  @header
end

#rowsObject (readonly)

The header row, the body rows, and the currently selected row index, respectively.



28
29
30
# File 'lib/charming/presentation/components/table.rb', line 28

def rows
  @rows
end

#selected_indexObject (readonly)

The header row, the body rows, and the currently selected row index, respectively.



28
29
30
# File 'lib/charming/presentation/components/table.rb', line 28

def selected_index
  @selected_index
end

Instance Method Details

#handle_key(event) ⇒ Object

Handles key events. Returns ‘[:selected, row]` on Enter; otherwise delegates to the KeyboardHandler for navigation keys.



43
44
45
46
47
48
49
50
# File 'lib/charming/presentation/components/table.rb', line 43

def handle_key(event)
  return nil if rows.empty?

  case Charming.key_of(event)
  when :enter then [:selected, selected_row]
  else super
  end
end

#handle_mouse(event) ⇒ Object

Handles mouse events: a click within the body area selects the clicked row. Returns :handled on a successful click.



54
55
56
57
58
59
60
61
62
63
# File 'lib/charming/presentation/components/table.rb', line 54

def handle_mouse(event)
  return nil if rows.empty?
  return nil unless event.respond_to?(:click?) && event.click?

  clicked = event.y - HEADER_HEIGHT
  return nil if clicked.negative? || clicked >= rows.length

  @selected_index = clicked
  :handled
end

#renderObject

Renders the table to a string. Returns a placeholder when both header and rows are empty.



71
72
73
74
75
76
77
78
79
80
# File 'lib/charming/presentation/components/table.rb', line 71

def render
  return "(empty table)" if header.empty? && rows.empty?

  normalized = rows.map { |row| normalize_row(row) }
  lines = TTY::Table.new(header: header, rows: normalized)
    .render(:unicode)
    .lines(chomp: true)

  compact_layout(lines)
end

#selected_rowObject

Returns the currently selected row, or nil when the table is empty.



66
67
68
# File 'lib/charming/presentation/components/table.rb', line 66

def selected_row
  rows[selected_index]
end