1use ansiq_core::{
2 BlockFrame, Element, ElementKind, Layout, Length, Line, SelectHandler, Span, Style, TabsProps,
3};
4
5use crate::Block;
6
7pub struct Tabs<Message = ()> {
8 block: Option<BlockFrame>,
9 titles: Vec<Line>,
10 selected: Option<usize>,
11 selection_explicit: bool,
12 highlight_style: Style,
13 divider: Span,
14 padding_left: Line,
15 padding_right: Line,
16 on_select: Option<SelectHandler<Message>>,
17 layout: Layout,
18 style: Style,
19 focusable: bool,
20}
21
22impl<Message> Default for Tabs<Message> {
23 fn default() -> Self {
24 Self {
25 block: None,
26 titles: Vec::new(),
27 selected: None,
28 selection_explicit: false,
29 highlight_style: Style::default().reversed(true),
30 divider: Span::raw(ansiq_core::symbols::line::VERTICAL),
31 padding_left: Line::from(" "),
32 padding_right: Line::from(" "),
33 on_select: None,
34 layout: Layout {
35 width: Length::Fill,
36 height: Length::Fixed(1),
37 },
38 style: Style::default(),
39 focusable: false,
40 }
41 }
42}
43
44impl Tabs<()> {
45 pub fn new<I, T>(titles: I) -> Self
46 where
47 I: IntoIterator<Item = T>,
48 T: Into<Line>,
49 {
50 Self::default().titles(titles)
51 }
52}
53
54impl<Message> Tabs<Message> {
55 pub fn block(mut self, block: Block<Message>) -> Self {
56 self.block = Some(block.into_frame());
57 self
58 }
59
60 pub fn tab<T>(mut self, title: T) -> Self
61 where
62 T: Into<Line>,
63 {
64 self.titles.push(title.into());
65 if self.titles.len() == 1 && self.selected.is_none() {
66 self.selected = Some(0);
67 }
68 self
69 }
70
71 pub fn titles<I, T>(mut self, titles: I) -> Self
72 where
73 I: IntoIterator<Item = T>,
74 T: Into<Line>,
75 {
76 self.titles = titles.into_iter().map(Into::into).collect();
77 self.selected = if self.titles.is_empty() {
78 None
79 } else {
80 self.selected
81 .map(|selected| selected.min(self.titles.len() - 1))
82 .or(Some(0))
83 };
84 self
85 }
86
87 pub fn selected(mut self, selected: Option<usize>) -> Self {
88 self.selected = selected;
89 self.selection_explicit = true;
90 self
91 }
92
93 pub fn select<T>(mut self, selected: T) -> Self
94 where
95 T: Into<Option<usize>>,
96 {
97 self.selected = selected.into();
98 self.selection_explicit = true;
99 self
100 }
101
102 pub fn highlight_style<S: Into<Style>>(mut self, style: S) -> Self {
103 self.highlight_style = style.into();
104 self
105 }
106
107 pub fn divider<D: Into<Span>>(mut self, divider: D) -> Self {
108 self.divider = divider.into();
109 self
110 }
111
112 pub fn padding<L, R>(mut self, left: L, right: R) -> Self
113 where
114 L: Into<Line>,
115 R: Into<Line>,
116 {
117 self.padding_left = left.into();
118 self.padding_right = right.into();
119 self
120 }
121
122 pub fn padding_left<P: Into<Line>>(mut self, left: P) -> Self {
123 self.padding_left = left.into();
124 self
125 }
126
127 pub fn padding_right<P: Into<Line>>(mut self, right: P) -> Self {
128 self.padding_right = right.into();
129 self
130 }
131
132 pub fn on_select<NextMessage, F>(self, handler: F) -> Tabs<NextMessage>
133 where
134 F: FnMut(usize) -> Option<NextMessage> + 'static,
135 {
136 Tabs {
137 block: self.block,
138 titles: self.titles,
139 selected: self.selected,
140 selection_explicit: self.selection_explicit,
141 highlight_style: self.highlight_style,
142 divider: self.divider,
143 padding_left: self.padding_left,
144 padding_right: self.padding_right,
145 on_select: Some(std::boxed::Box::new(handler)),
146 layout: self.layout,
147 style: self.style,
148 focusable: true,
149 }
150 }
151
152 pub fn layout(mut self, layout: Layout) -> Self {
153 self.layout = layout;
154 self
155 }
156
157 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
158 self.style = style.into();
159 self
160 }
161
162 pub fn build(self) -> Element<Message> {
163 Element::new(ElementKind::Tabs(TabsProps {
164 block: self.block,
165 titles: self.titles,
166 selected: self.selected,
167 selection_explicit: self.selection_explicit,
168 highlight_style: self.highlight_style,
169 divider: self.divider,
170 padding_left: self.padding_left,
171 padding_right: self.padding_right,
172 on_select: self.on_select,
173 }))
174 .with_layout(self.layout)
175 .with_style(self.style)
176 .with_focusable(self.focusable)
177 }
178}
179
180impl<Item> FromIterator<Item> for Tabs<()>
181where
182 Item: Into<Line>,
183{
184 fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
185 Self::new(iter)
186 }
187}