use crate::components::stats::table::TeamOrPlayer;
use crate::components::stats::{STATS_DEFAULT_COL_WIDTH, STATS_FIRST_COL_WIDTH};
use crate::state::stats::{ActivePane, StatsState};
use crate::ui::styling::{
DimStyle, avg_style, border_style, dim_style, era_style, header_style, selected_style,
};
use mlbt_api::client::{Qualification, StatGroup};
use tui::prelude::*;
use tui::widgets::{Block, BorderType, Borders, Cell, Padding, Paragraph, Row, Table};
pub const STATS_OPTIONS_WIDTH: u16 = 36;
pub struct StatsDataWidget {}
impl StatefulWidget for StatsDataWidget {
type State = StatsState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let table = state.generate_table();
let (header, _, rows) = table.as_ref();
let mut avg_idx = None;
let mut era_idx = None;
for (i, name) in header.iter().enumerate() {
match name.as_str() {
"AVG" => avg_idx = Some(i),
"ERA" => era_idx = Some(i),
_ => {}
}
}
let sort_column = state
.table
.sorting
.column_name
.as_deref()
.unwrap_or_default();
let header = header
.iter()
.map(|name| {
if name == sort_column {
Cell::from(format!(
"{name} {}",
state.table.sorting.order.arrow_symbol()
))
.style(selected_style())
} else {
Cell::from(name.as_str())
}
})
.collect::<Row>()
.height(1)
.style(header_style());
let rows: Vec<Row> = rows
.iter()
.map(|row| {
row.iter()
.enumerate()
.map(|(i, cell)| {
let style = if Some(i) == avg_idx {
avg_style(cell)
} else if Some(i) == era_idx {
era_style(cell)
} else {
cell.as_str().dim_or_default()
};
Cell::from(cell.as_str()).style(style)
})
.collect::<Row>()
})
.collect();
let active = state.table.columns.values().filter(|v| v.active).count();
let mut constraints = vec![Constraint::Length(STATS_DEFAULT_COL_WIDTH); active];
if active == 0 {
constraints.push(Constraint::Length(STATS_FIRST_COL_WIDTH));
} else {
constraints[0] = Constraint::Length(STATS_FIRST_COL_WIDTH);
}
let mut t = Table::new(rows, constraints)
.header(header)
.column_spacing(0)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(border_style())
.padding(Padding::new(1, 1, 0, 0))
.title(Span::styled(
state.date_selector.format_date_border_title(),
selected_style(),
)),
);
if state.active_pane == ActivePane::Data {
t = t.row_highlight_style(selected_style());
}
state.visible_rows = area.height.saturating_sub(3) as usize;
StatefulWidget::render(t, area, buf, &mut state.data_state);
}
}
pub struct StatsOptionsWidget {}
impl StatefulWidget for StatsOptionsWidget {
type State = StatsState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let [stats_rect, options_rect] =
Layout::vertical([Constraint::Length(5), Constraint::Percentage(100)]).areas(area);
let (hitting_style, pitching_style) = match state.stat_type.group {
StatGroup::Pitching => (Style::default(), selected_style()),
StatGroup::Hitting => (selected_style(), Style::default()),
};
let (team_style, player_style) = match state.stat_type.team_player {
TeamOrPlayer::Player => (Style::default(), selected_style()),
TeamOrPlayer::Team => (selected_style(), Style::default()),
};
let is_team = state.stat_type.team_player == TeamOrPlayer::Team;
let dim_if_team = if is_team {
dim_style()
} else {
Style::default()
};
let (all_style, qualified_style) = if is_team {
(dim_if_team, dim_if_team)
} else {
match state.stat_type.qualification {
Qualification::All => (selected_style(), Style::default()),
Qualification::Qualified => (Style::default(), selected_style()),
}
};
let block = Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(border_style());
let inner = block.inner(stats_rect);
block.render(stats_rect, buf);
let [left_area, divider_area, right_area] = Layout::horizontal([
Constraint::Fill(1),
Constraint::Length(3),
Constraint::Fill(1),
])
.areas(inner);
Paragraph::new(vec![
Line::from(Span::styled("hitting", hitting_style)),
Line::from(Span::styled("team", team_style)),
Line::from(Span::styled("all", all_style)),
])
.alignment(Alignment::Right)
.render(left_area, buf);
Paragraph::new(vec![
Line::from(" | "),
Line::from(" | "),
Line::from(" | ").style(dim_if_team),
])
.alignment(Alignment::Center)
.render(divider_area, buf);
Paragraph::new(vec![
Line::from(Span::styled("pitching", pitching_style)),
Line::from(Span::styled("player", player_style)),
Line::from(Span::styled("qualified", qualified_style)),
])
.alignment(Alignment::Left)
.render(right_area, buf);
let mut options = Vec::new();
for (name, stat) in &state.table.columns {
let selected = if stat.active { "[X]" } else { "[ ]" };
options.push(Row::new(vec![
selected,
name.as_str(),
stat.description.as_str(),
]));
}
let widths = [
Constraint::Length(4),
Constraint::Length(6),
Constraint::Length(25),
];
let mut t = Table::new(options, widths).column_spacing(0).block(
Block::default()
.padding(Padding::new(1, 1, 0, 0))
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(border_style()),
);
if state.active_pane == ActivePane::Options {
t = t.row_highlight_style(selected_style());
}
StatefulWidget::render(t, options_rect, buf, &mut state.options_state);
}
}