Class: Charming::Components::Table

Inherits:
Charming::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).



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

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.



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

def header
  @header
end

#rowsObject (readonly)

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



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

def rows
  @rows
end

#selected_indexObject (readonly)

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



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

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.



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

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.



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

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.



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

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.



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

def selected_row
  rows[selected_index]
end