use crate::components::{Box as RnkBox, Line, Span, Text};
use crate::core::{Color, Element, Style};
#[derive(Debug, Clone)]
pub struct Tab {
pub title: String,
pub style: Option<Style>,
}
impl Tab {
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
style: None,
}
}
pub fn style(mut self, style: Style) -> Self {
self.style = Some(style);
self
}
}
impl<T: Into<String>> From<T> for Tab {
fn from(s: T) -> Self {
Tab::new(s)
}
}
#[derive(Debug, Clone)]
pub struct Tabs {
tabs: Vec<Tab>,
selected: usize,
highlight_style: Style,
normal_style: Style,
divider: String,
key: Option<String>,
}
impl Tabs {
pub fn new() -> Self {
let mut highlight_style = Style::new();
highlight_style.bold = true;
Self {
tabs: Vec::new(),
selected: 0,
highlight_style,
normal_style: Style::new(),
divider: " | ".to_string(),
key: None,
}
}
pub fn from_items<I, T>(items: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Tab>,
{
let mut tabs = Self::new();
tabs.tabs = items.into_iter().map(|t| t.into()).collect();
tabs
}
pub fn tabs<I, T>(mut self, tabs: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Tab>,
{
self.tabs = tabs.into_iter().map(|t| t.into()).collect();
self
}
pub fn tab(mut self, tab: impl Into<Tab>) -> Self {
self.tabs.push(tab.into());
self
}
pub fn selected(mut self, index: usize) -> Self {
self.selected = index;
self
}
pub fn highlight_style(mut self, style: Style) -> Self {
self.highlight_style = style;
self
}
pub fn highlight_color(mut self, color: Color) -> Self {
self.highlight_style.color = Some(color);
self
}
pub fn normal_style(mut self, style: Style) -> Self {
self.normal_style = style;
self
}
pub fn divider(mut self, divider: impl Into<String>) -> Self {
self.divider = divider.into();
self
}
pub fn key(mut self, key: impl Into<String>) -> Self {
self.key = Some(key.into());
self
}
pub fn len(&self) -> usize {
self.tabs.len()
}
pub fn is_empty(&self) -> bool {
self.tabs.is_empty()
}
pub fn into_element(self) -> Element {
let mut spans = Vec::new();
for (i, tab) in self.tabs.iter().enumerate() {
if i > 0 {
spans.push(Span::new(&self.divider).dim());
}
let is_selected = i == self.selected;
let mut span = Span::new(&tab.title);
if is_selected {
if let Some(color) = self.highlight_style.color {
span = span.color(color);
}
if self.highlight_style.bold {
span = span.bold();
}
if self.highlight_style.underline {
span = span.underline();
}
if self.highlight_style.inverse {
span = span.inverse();
}
} else {
if let Some(color) = self.normal_style.color {
span = span.color(color);
}
if self.normal_style.dim {
span = span.dim();
}
}
spans.push(span);
}
let text = Text::line(Line::from_spans(spans));
let mut container = RnkBox::new().child(text.into_element());
if let Some(key) = self.key {
container = container.key(key);
}
container.into_element()
}
}
impl Default for Tabs {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tab_creation() {
let tab = Tab::new("Home");
assert_eq!(tab.title, "Home");
}
#[test]
fn test_tabs_creation() {
let tabs = Tabs::from_items(vec!["Home", "Settings", "About"]);
assert_eq!(tabs.len(), 3);
}
#[test]
fn test_tabs_selected() {
let tabs = Tabs::from_items(vec!["A", "B", "C"]).selected(1);
assert_eq!(tabs.selected, 1);
}
#[test]
fn test_tabs_divider() {
let tabs = Tabs::new().tab("Home").tab("Settings").divider(" │ ");
assert_eq!(tabs.divider, " │ ");
}
}