Skip to content

Commit 4334509

Browse files
Refactor DataTab to use scrollable DataTable for data preview (#24)
**Summary** Replace the Rich `Table` preview with Textual’s `DataTable`, wrapping it in `HorizontalScroll` so wide Parquet schemas stay accessible while preserving formatting. This is just one possible implementation. **Changes** - Swap `_create_data_table` to build a Textual `DataTable` (cursor support, zebra stripes, border styling, NULL highlighting). - Size columns via header-based `min_width` and wrap the widget in `HorizontalScroll` inside `compose` so wide schemas remain accessible. - Yield `Static` error/empty states through the Textual compose API to match the new widget structure. Closes #23 --------- Co-authored-by: Raúl Cumplido <[email protected]>
1 parent c0adad6 commit 4334509

File tree

1 file changed

+55
-42
lines changed

1 file changed

+55
-42
lines changed

src/datanomy/tui/parquet.py

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from rich.table import Table
66
from rich.text import Text
77
from textual.app import ComposeResult
8-
from textual.widgets import Static
8+
from textual.containers import HorizontalScroll, Vertical
9+
from textual.widgets import DataTable, Static
910

1011
from datanomy.reader.parquet import ParquetReader
1112
from datanomy.tui.common import create_column_grid
@@ -736,6 +737,7 @@ def __init__(self, reader: ParquetReader, num_rows: int = 50) -> None:
736737
"""
737738
super().__init__(reader)
738739
self.num_rows = num_rows
740+
self.id = "data-content"
739741

740742
@staticmethod
741743
def _format_value(value: Any, max_length: int = 50) -> str:
@@ -782,80 +784,91 @@ def _read_data(self) -> tuple[Any, int, int]:
782784

783785
return table, num_rows_display, total_rows
784786

785-
def _create_data_table(self, table: Any, num_rows_display: int) -> Panel:
787+
def _create_data_table(self, table: Any, num_rows_display: int) -> DataTable:
786788
"""
787-
Create Rich table with data rows.
789+
Create a Textual ``DataTable`` widget populated with preview rows.
788790
789791
Parameters
790792
----------
791-
table: PyArrow table
792-
num_rows_display: Number of rows to display
793+
table: PyArrow table slice for preview rendering
794+
num_rows_display: Number of rows to include in the table
793795
794796
Returns
795797
-------
796-
Panel: Rich Panel containing data table
797-
"""
798-
# Create Rich table
799-
data_table = Table(
800-
show_header=True,
801-
header_style="bold cyan",
802-
border_style="dim",
803-
expand=False,
804-
box=None,
805-
)
798+
DataTable: Configured widget ready for display
799+
"""
800+
801+
data_table: DataTable = DataTable(id="data-preview-table", zebra_stripes=True)
802+
data_table.border_title = "Data Preview"
803+
data_table.styles.border = ("round", "cyan")
804+
data_table.styles.width = "auto"
805+
806+
columns = list(table.schema.names)
807+
if not columns:
808+
return data_table
809+
810+
min_width = max(80, sum(max(12, len(name) + 2) for name in columns))
811+
data_table.styles.min_width = min_width
806812

807-
# Add columns
808-
for col_name in table.schema.names:
809-
data_table.add_column(col_name, style="white", no_wrap=False)
813+
data_table.add_columns(*columns)
810814

811-
# Add rows
812-
for i in range(num_rows_display):
815+
for row_idx in range(num_rows_display):
813816
row_values: list[str | Text] = []
814-
for col_name in table.schema.names:
815-
value = table[col_name][i].as_py()
817+
for name in columns:
818+
value = table[name][row_idx].as_py()
816819
formatted_value = self._format_value(value)
817-
818-
# Style NULL values differently
819820
if value is None:
820821
row_values.append(Text(formatted_value, style="dim yellow"))
821822
else:
822823
row_values.append(formatted_value)
823-
824824
data_table.add_row(*row_values)
825825

826-
# Wrap table in panel
827-
return Panel(
828-
data_table,
829-
title="[cyan]Data Preview[/cyan]",
830-
border_style="cyan",
831-
)
826+
return data_table
832827

833-
def render_tab_content(self) -> Group:
828+
def compose(self) -> ComposeResult:
834829
"""
835-
Render data preview table.
830+
Compose widgets for the data preview tab.
836831
837-
Returns
838-
-------
839-
Group: Rich renderable showing data rows
832+
Yields
833+
------
834+
ComposeResult: Child widgets making up the tab content
840835
"""
841-
# Read data from the Parquet file
836+
842837
try:
843838
table, num_rows_display, total_rows = self._read_data()
844839
except Exception as e:
845840
error_text = Text()
846841
error_text.append(f"Error reading data: {e}", style="red")
847-
return Group(Panel(error_text, title="[red]Error[/red]"))
842+
yield Static(Panel(error_text, title="[red]Error[/red]"), id="data-content")
843+
return
844+
845+
columns = list(table.schema.names)
846+
data_widget: DataTable | None = None
847+
empty_panel: Panel | None = None
848+
849+
if columns:
850+
data_widget = self._create_data_table(table, num_rows_display)
851+
else:
852+
empty_text = Text("Parquet table has no columns", style="yellow")
853+
empty_panel = Panel(
854+
empty_text,
855+
title="[cyan]Data Preview[/cyan]",
856+
border_style="cyan",
857+
)
848858

849-
# Create header info
850859
header_text = Text()
851860
header_text.append(
852861
f"Showing {num_rows_display:,} of {total_rows:,} rows", style="cyan bold"
853862
)
854863

855-
# Create data table
856-
table_panel = self._create_data_table(table, num_rows_display)
864+
with Vertical(id="data-content"):
865+
yield Static(header_text)
857866

858-
return Group(header_text, Text(), table_panel)
867+
if data_widget is not None:
868+
with HorizontalScroll():
869+
yield data_widget
870+
elif empty_panel is not None:
871+
yield Static(empty_panel)
859872

860873

861874
class MetadataTab(BaseParquetTab):

0 commit comments

Comments
 (0)