use std::io::{self, Write};
use eye_declare::{
BorderType, Canvas, DataChildren, Elements, InlineRenderer, VStack, View, component, element,
props,
};
use ratatui_core::{
buffer::Buffer,
layout::Rect,
style::{Color, Modifier, Style},
widgets::Widget,
};
use ratatui_widgets::paragraph::Paragraph;
#[derive(Clone)]
struct Item {
label: String,
value: String,
style: Style,
}
impl Item {
fn builder() -> ItemBuilder {
ItemBuilder {
label: String::new(),
value: String::new(),
style: Style::default(),
}
}
}
struct ItemBuilder {
label: String,
value: String,
style: Style,
}
impl ItemBuilder {
fn label(mut self, l: impl Into<String>) -> Self {
self.label = l.into();
self
}
fn value(mut self, v: impl Into<String>) -> Self {
self.value = v.into();
self
}
fn style(mut self, s: Style) -> Self {
self.style = s;
self
}
fn build(self) -> Item {
Item {
label: self.label,
value: self.value,
style: self.style,
}
}
}
enum TableChild {
Item(Item),
}
impl From<Item> for TableChild {
fn from(item: Item) -> Self {
TableChild::Item(item)
}
}
#[props]
struct Table {
title: String,
}
#[component(props = Table, children = DataChildren<TableChild>)]
fn table(props: &Table, children: &DataChildren<TableChild>) -> Elements {
let items: Vec<&Item> = children
.as_slice()
.iter()
.map(|c| match c {
TableChild::Item(item) => item,
})
.collect();
let max_label = items.iter().map(|i| i.label.len()).max().unwrap_or(0);
let lines: Vec<ratatui_core::text::Line<'static>> = items
.iter()
.map(|item| {
let padded_label = format!("{:>width$}", item.label, width = max_label);
ratatui_core::text::Line::from(vec![
ratatui_core::text::Span::styled(
padded_label,
Style::default().add_modifier(Modifier::BOLD),
),
ratatui_core::text::Span::styled(" ", Style::default()),
ratatui_core::text::Span::styled(item.value.clone(), item.style),
])
})
.collect();
let title = props.title.clone();
element! {
View(
border: BorderType::Rounded,
border_style: Style::default().fg(Color::DarkGray),
title: title,
title_style: Style::default().fg(Color::White).add_modifier(Modifier::BOLD),
padding_left: Some(eye_declare::Cells(1)),
padding_right: Some(eye_declare::Cells(1)),
) {
Canvas(render_fn: move |area: Rect, buf: &mut Buffer| {
Paragraph::new(lines.clone()).render(area, buf);
})
}
}
}
fn main() -> io::Result<()> {
let (width, _) = crossterm::terminal::size()?;
let mut r = InlineRenderer::new(width);
let mut stdout = io::stdout();
let container = r.push(VStack);
let els = element! {
Table(title: "System Info") {
Item(label: "OS", value: "macOS 15.3", style: Style::default().fg(Color::Cyan))
Item(label: "Rust", value: "1.86.0", style: Style::default().fg(Color::Yellow))
Item(label: "Terminal", value: format!("{}×cols", width), style: Style::default().fg(Color::Green))
}
Table(title: "Empty Table")
Table(title: "Status") {
Item(label: "Build", value: "passing", style: Style::default().fg(Color::Green))
Item(label: "Tests", value: "243 passed", style: Style::default().fg(Color::Green))
Item(label: "Coverage", value: "87%", style: Style::default().fg(Color::Yellow))
}
};
r.rebuild(container, els);
let output = r.render();
stdout.write_all(&output)?;
stdout.flush()?;
println!();
Ok(())
}