151 lines
5.6 KiB
Rust
151 lines
5.6 KiB
Rust
// src/components/handlers/sidebar.rs
|
|
use ratatui::{
|
|
widgets::{Block, List, ListItem},
|
|
layout::{Rect, Direction, Layout, Constraint},
|
|
style::Style,
|
|
Frame,
|
|
};
|
|
use crate::config::colors::themes::Theme;
|
|
use common::proto::komp_ac::table_definition::{ProfileTreeResponse};
|
|
use ratatui::text::{Span, Line};
|
|
use crate::components::utils::text::truncate_string;
|
|
|
|
// Reduced sidebar width
|
|
const SIDEBAR_WIDTH: u16 = 20;
|
|
|
|
// --- Icons ---
|
|
const ICON_PROFILE: &str = "📁";
|
|
const ICON_TABLE: &str = "📄";
|
|
|
|
pub fn calculate_sidebar_layout(show_sidebar: bool, main_content_area: Rect) -> (Option<Rect>, Rect) {
|
|
if show_sidebar {
|
|
let chunks = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints([
|
|
Constraint::Length(SIDEBAR_WIDTH),
|
|
Constraint::Min(0),
|
|
])
|
|
.split(main_content_area);
|
|
(Some(chunks[0]), chunks[1])
|
|
} else {
|
|
(None, main_content_area)
|
|
}
|
|
}
|
|
|
|
pub fn render_sidebar(
|
|
f: &mut Frame,
|
|
area: Rect,
|
|
theme: &Theme,
|
|
profile_tree: &ProfileTreeResponse,
|
|
selected_profile: &Option<String>,
|
|
) {
|
|
let sidebar_block = Block::default().style(Style::default().bg(theme.bg));
|
|
let mut items = Vec::new();
|
|
let profile_name_available_width = (SIDEBAR_WIDTH as usize).saturating_sub(3);
|
|
let table_name_available_width = (SIDEBAR_WIDTH as usize).saturating_sub(5);
|
|
|
|
if let Some(profile_name) = selected_profile {
|
|
// Find the selected profile in the tree
|
|
if let Some(profile) = profile_tree
|
|
.profiles
|
|
.iter()
|
|
.find(|p| &p.name == profile_name)
|
|
{
|
|
// Add profile name as header
|
|
items.push(ListItem::new(Line::from(vec![
|
|
Span::styled(format!("{} ", ICON_PROFILE), Style::default().fg(theme.accent)),
|
|
Span::styled(
|
|
truncate_string(&profile.name, profile_name_available_width),
|
|
Style::default().fg(theme.highlight)
|
|
),
|
|
])));
|
|
|
|
// List tables for the selected profile
|
|
for table in &profile.tables {
|
|
// Get table name without year prefix to save space
|
|
let display_name = if table.name.starts_with("2025_") {
|
|
&table.name[5..] // Skip "2025_" prefix
|
|
} else {
|
|
&table.name
|
|
};
|
|
items.push(ListItem::new(Line::from(vec![
|
|
Span::raw(" "), // Indentation
|
|
Span::styled(format!("{} ", ICON_TABLE), Style::default().fg(theme.secondary)),
|
|
Span::styled(
|
|
truncate_string(display_name, table_name_available_width),
|
|
theme.fg
|
|
),
|
|
])));
|
|
}
|
|
}
|
|
} else {
|
|
// Show full profile tree when no profile is selected (compact version)
|
|
for (profile_idx, profile) in profile_tree.profiles.iter().enumerate() {
|
|
// Profile header - more compact
|
|
items.push(ListItem::new(Line::from(vec![
|
|
Span::styled(format!("{} ", ICON_PROFILE), Style::default().fg(theme.accent)),
|
|
Span::styled(
|
|
&profile.name,
|
|
Style::default().fg(theme.highlight)
|
|
),
|
|
])));
|
|
// Tables with compact prefixes
|
|
for (table_idx, table) in profile.tables.iter().enumerate() {
|
|
let is_last_table = table_idx == profile.tables.len() - 1;
|
|
let is_last_profile = profile_idx == profile_tree.profiles.len() - 1;
|
|
|
|
// Shorter prefix characters
|
|
let prefix = match (is_last_profile, is_last_table) {
|
|
(true, true) => " └",
|
|
(true, false) => " ├",
|
|
(false, true) => "│└",
|
|
(false, false) => "│├",
|
|
};
|
|
|
|
// Get table name without year prefix to save space
|
|
let display_name = if table.name.starts_with("2025_") {
|
|
&table.name[5..] // Skip "2025_" prefix
|
|
} else {
|
|
&table.name
|
|
};
|
|
|
|
// Adjust available width if dependency arrow is shown
|
|
let current_table_available_width = if !table.depends_on.is_empty() {
|
|
table_name_available_width.saturating_sub(1)
|
|
} else {
|
|
table_name_available_width
|
|
};
|
|
|
|
let line = vec![
|
|
Span::styled(prefix, Style::default().fg(theme.fg)),
|
|
Span::styled(format!("{} ", ICON_TABLE), Style::default().fg(theme.secondary)),
|
|
Span::styled(truncate_string(display_name, current_table_available_width), Style::default().fg(theme.fg)),
|
|
];
|
|
|
|
items.push(ListItem::new(Line::from(line)));
|
|
}
|
|
|
|
// Compact separator between profiles
|
|
if profile_idx < profile_tree.profiles.len() - 1 {
|
|
items.push(ListItem::new(Line::from(
|
|
Span::styled("│", Style::default().fg(theme.secondary))
|
|
)));
|
|
}
|
|
}
|
|
|
|
if profile_tree.profiles.is_empty() {
|
|
items.push(ListItem::new(Span::styled(
|
|
"No profiles",
|
|
Style::default().fg(theme.secondary)
|
|
)));
|
|
}
|
|
}
|
|
|
|
let list = List::new(items)
|
|
.block(sidebar_block)
|
|
.highlight_style(Style::default().fg(theme.highlight))
|
|
.highlight_symbol(">");
|
|
|
|
f.render_widget(list, area);
|
|
}
|