use strum::{Display, EnumString};
use super::ListItem;
use crate::{
prelude::*,
style::Styled,
widgets::{Block, HighlightSpacing},
};
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct List<'a> {
pub(crate) block: Option<Block<'a>>,
pub(crate) items: Vec<ListItem<'a>>,
pub(crate) style: Style,
pub(crate) direction: ListDirection,
pub(crate) highlight_style: Style,
pub(crate) highlight_symbol: Option<&'a str>,
pub(crate) repeat_highlight_symbol: bool,
pub(crate) highlight_spacing: HighlightSpacing,
pub(crate) scroll_padding: usize,
}
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ListDirection {
#[default]
TopToBottom,
BottomToTop,
}
impl<'a> List<'a> {
pub fn new<T>(items: T) -> Self
where
T: IntoIterator,
T::Item: Into<ListItem<'a>>,
{
Self {
block: None,
style: Style::default(),
items: items.into_iter().map(Into::into).collect(),
direction: ListDirection::default(),
..Self::default()
}
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn items<T>(mut self, items: T) -> Self
where
T: IntoIterator,
T::Item: Into<ListItem<'a>>,
{
self.items = items.into_iter().map(Into::into).collect();
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
self.style = style.into();
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn highlight_symbol(mut self, highlight_symbol: &'a str) -> Self {
self.highlight_symbol = Some(highlight_symbol);
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn highlight_style<S: Into<Style>>(mut self, style: S) -> Self {
self.highlight_style = style.into();
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn repeat_highlight_symbol(mut self, repeat: bool) -> Self {
self.repeat_highlight_symbol = repeat;
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn highlight_spacing(mut self, value: HighlightSpacing) -> Self {
self.highlight_spacing = value;
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn direction(mut self, direction: ListDirection) -> Self {
self.direction = direction;
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn scroll_padding(mut self, padding: usize) -> Self {
self.scroll_padding = padding;
self
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
}
impl<'a> Styled for List<'a> {
type Item = Self;
fn style(&self) -> Style {
self.style
}
fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
self.style(style)
}
}
impl<'a> Styled for ListItem<'a> {
type Item = Self;
fn style(&self) -> Style {
self.style
}
fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
self.style(style)
}
}
impl<'a, Item> FromIterator<Item> for List<'a>
where
Item: Into<ListItem<'a>>,
{
fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
Self::new(iter)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn collect_list_from_iterator() {
let collected: List = (0..3).map(|i| format!("Item{i}")).collect();
let expected = List::new(["Item0", "Item1", "Item2"]);
assert_eq!(collected, expected);
}
#[test]
fn can_be_stylized() {
assert_eq!(
List::new::<Vec<&str>>(vec![])
.black()
.on_white()
.bold()
.not_dim()
.style,
Style::default()
.fg(Color::Black)
.bg(Color::White)
.add_modifier(Modifier::BOLD)
.remove_modifier(Modifier::DIM)
);
}
}