lariv-rs 0.1.0

Compile-time plugin web application framework built on Axum, SeaORM, Maud, and HTMX
Documentation
//! Label wrappers around child markup.

use maud::{Markup, PreEscaped, html};

pub struct Label<'a> {
    pub label: &'a str,
    pub hint: Option<&'a str>,
    pub children: Markup,
}

/// `<div class="my-1"><label class="label text-sm font-bold flex flex-col …">Title …</label></div>`
pub fn label(label: &str, children: Markup) -> Markup {
    label_hint(label, None, children)
}

pub fn label_hint(label: &str, hint: Option<&str>, children: Markup) -> Markup {
    html! {
        div class="my-1 w-full" {
            label class="label text-sm font-bold flex flex-col items-stretch gap-1 w-full min-w-0" {
                span class="inline-flex items-center gap-1" {
                    (label)
                    (label_hint_icon(hint))
                }
            }
            (children)
        }
    }
}

fn label_hint_icon(hint: Option<&str>) -> Markup {
    match hint.filter(|text| !text.is_empty()) {
        Some(text) => {
            let body = html! { (text) }.into_string();
            PreEscaped(format!(
                r#"<span class="relative inline-flex align-middle" x-data="{{ hintOpen: false, hintLeave: null }}" x-on:mouseenter="clearTimeout(hintLeave); hintOpen = true" x-on:mouseleave="hintLeave = setTimeout(() => hintOpen = false, 120)"><span class="cursor-help opacity-60 text-sm font-normal leading-none" aria-label="Field information">🛈</span><div x-show="hintOpen" x-cloak class="absolute top-full left-0 pt-1 z-50 w-max max-w-lg"><div class="max-h-64 overflow-y-auto overflow-x-hidden rounded-box border border-base-300 bg-base-100 text-base-content px-3 py-2 text-left text-xs leading-snug whitespace-pre-line shadow-md">{body}</div></div></span>"#
            ))
        }
        None => html! {},
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use maud::html;

    fn markup(m: Markup) -> String {
        m.into_string()
    }

    #[test]
    fn label_without_hint_omits_icon() {
        let html = markup(label("Status", html! { "ok" }));
        assert!(html.contains("Status"));
        assert!(!html.contains("Status:"));
        assert!(!html.contains("🛈"));
        assert!(!html.contains("data-tip"));
        assert!(!html.contains("hintOpen"));
    }

    #[test]
    fn label_with_hint_renders_tooltip_icon() {
        let html = markup(label_hint(
            "Notes",
            Some("Internal only."),
            html! { "Example" },
        ));
        assert!(html.contains("label text-sm font-bold"));
        assert!(html.contains("Notes"));
        assert!(!html.contains("Notes:"));
        assert!(html.contains("🛈"));
        assert!(html.contains("hintOpen"));
        assert!(html.contains("Internal only."));
    }
}