use blinc_theme::{ColorToken, ThemeState};
use crate::text::{text, Text};
const HEADING_CONFIG: [(f32, HeadingWeight); 6] = [
(32.0, HeadingWeight::Bold), (24.0, HeadingWeight::Bold), (20.0, HeadingWeight::SemiBold), (18.0, HeadingWeight::SemiBold), (16.0, HeadingWeight::Medium), (14.0, HeadingWeight::Medium), ];
#[derive(Clone, Copy)]
enum HeadingWeight {
Medium,
SemiBold,
Bold,
}
pub fn h1(content: impl Into<String>) -> Text {
heading(1, content)
}
pub fn h2(content: impl Into<String>) -> Text {
heading(2, content)
}
pub fn h3(content: impl Into<String>) -> Text {
heading(3, content)
}
pub fn h4(content: impl Into<String>) -> Text {
heading(4, content)
}
pub fn h5(content: impl Into<String>) -> Text {
heading(5, content)
}
pub fn h6(content: impl Into<String>) -> Text {
heading(6, content)
}
pub fn heading(level: u8, content: impl Into<String>) -> Text {
let idx = (level.saturating_sub(1).min(5)) as usize;
let (size, weight) = HEADING_CONFIG[idx];
let semantic = match level {
1 => "h1",
2 => "h2",
3 => "h3",
4 => "h4",
5 => "h5",
_ => "h6",
};
let t = text(content).size(size).semantic_type(semantic);
match weight {
HeadingWeight::Medium => t.medium(),
HeadingWeight::SemiBold => t.semibold(),
HeadingWeight::Bold => t.bold(),
}
}
pub fn b(content: impl Into<String>) -> Text {
text(content).bold().no_wrap().v_baseline()
}
pub fn strong(content: impl Into<String>) -> Text {
b(content)
}
pub fn span(content: impl Into<String>) -> Text {
text(content).no_wrap().v_baseline().semantic_type("span")
}
pub fn small(content: impl Into<String>) -> Text {
text(content).size(12.0).no_wrap().v_baseline()
}
pub fn label(content: impl Into<String>) -> Text {
text(content).size(14.0).medium().no_wrap().v_baseline()
}
pub fn muted(content: impl Into<String>) -> Text {
let theme = ThemeState::get();
text(content)
.color(theme.color(ColorToken::TextSecondary))
.no_wrap()
.v_baseline()
}
pub fn p(content: impl Into<String>) -> Text {
text(content).size(16.0).line_height(1.5).semantic_type("p")
}
pub fn caption(content: impl Into<String>) -> Text {
let theme = ThemeState::get();
text(content)
.size(12.0)
.color(theme.color(ColorToken::TextTertiary))
.no_wrap()
.v_baseline()
}
pub fn inline_code(content: impl Into<String>) -> Text {
let theme = ThemeState::get();
text(content)
.monospace()
.no_wrap()
.v_baseline()
.color(theme.color(ColorToken::TextPrimary))
}
use crate::div::{div, Div};
pub fn chained_text<const N: usize>(elements: [Text; N]) -> Div {
let mut container = div().flex_row().items_start().items_baseline();
for element in elements {
container = container.child(element);
}
container
}
#[cfg(test)]
mod tests {
use super::*;
use crate::div::ElementBuilder;
use crate::tree::LayoutTree;
use blinc_theme::ThemeState;
fn init_theme() {
let _ = ThemeState::try_get().unwrap_or_else(|| {
ThemeState::init_default();
ThemeState::get()
});
}
#[test]
fn test_headings() {
let mut tree = LayoutTree::new();
let _h1 = h1("Title").build(&mut tree);
let _h2 = h2("Subtitle").build(&mut tree);
let _h3 = h3("Section").build(&mut tree);
assert_eq!(tree.len(), 3);
}
#[test]
fn test_heading_levels() {
let _ = heading(1, "One");
let _ = heading(6, "Six");
let _ = heading(0, "Zero"); let _ = heading(10, "Ten"); }
#[test]
fn test_inline_helpers() {
init_theme();
let mut tree = LayoutTree::new();
let _bold = b("Bold").build(&mut tree);
let _strong = strong("Strong").build(&mut tree);
let _span = span("Span").build(&mut tree);
let _small = small("Small").build(&mut tree);
let _label = label("Label").build(&mut tree);
let _muted = muted("Muted").build(&mut tree);
let _para = p("Paragraph").build(&mut tree);
let _cap = caption("Caption").build(&mut tree);
assert_eq!(tree.len(), 8);
}
}