use crate::core::px;
use crate::view::prelude::*;
pub mod metrics {
use crate::core::{Pixels, px};
pub const ROW_HEIGHT: Pixels = px(44.0);
pub const TEXT_GAP: Pixels = px(4.0);
}
pub fn page_header(title: impl Into<String>, subtitle: impl Into<String>) -> Div {
let subtitle = subtitle.into();
let mut header = div().vstack().gap(metrics::TEXT_GAP).child(div().heading().child(title.into()));
if !subtitle.is_empty() {
header = header.child(div().caption().child(subtitle));
}
header
}
pub fn section(title: impl Into<String>, hint: impl Into<String>) -> Div {
let hint = hint.into();
let mut block = div().vstack().gap(metrics::TEXT_GAP).child(div().text_sm().child(title.into()));
if !hint.is_empty() {
block = block.child(div().caption().child(hint));
}
block
}
pub fn setting_row(title: impl Into<String>, hint: impl Into<String>, control: impl IntoElement) -> Div {
div()
.hstack()
.gap_3()
.min_h(metrics::ROW_HEIGHT)
.py(px(4.0))
.child(div().flexible().child(section(title, hint)))
.child(control)
}
pub fn field(label: impl Into<String>, control: impl IntoElement) -> Div {
div().vstack().gap(metrics::TEXT_GAP).child(div().text_sm().child(label.into())).child(control)
}
pub fn empty_state(title: impl Into<String>, hint: impl Into<String>) -> Div {
let hint = hint.into();
let mut state =
div().vstack().flex_1().center().gap(metrics::TEXT_GAP).child(div().text_base().child(title.into()));
if !hint.is_empty() {
state = state.child(div().caption().child(hint));
}
state
}
#[cfg(test)]
mod tests {
use super::*;
use crate::view::theme::Token;
#[test]
fn setting_row_reparte_descripcion_y_control() {
let mut row = setting_row("Tema", "Cambia todo", div().w_10());
assert_eq!(row.style().direction, Some(crate::view::style::Direction::Row));
assert_eq!(row.style().align_items, Some(crate::view::style::Align::Center));
assert_eq!(row.style().min_height, Some(crate::view::style::Length::Px(metrics::ROW_HEIGHT)));
}
#[test]
fn caption_y_muted_usan_el_token_secundario() {
let mut caption = div().caption();
assert_eq!(caption.style().text_color, Some(Token::TextMuted.into()));
assert_eq!(caption.style().font_size, Some(px(12.0)));
}
#[test]
fn empty_state_ocupa_el_hueco_y_centra() {
let mut empty = empty_state("Sin resultados", "Prueba con otra búsqueda");
let style = empty.style();
assert_eq!(style.flex_grow, Some(1.0));
assert_eq!(style.align_items, Some(crate::view::style::Align::Center));
assert_eq!(style.justify_content, Some(crate::view::style::Justify::Center));
}
#[test]
fn field_apila_etiqueta_y_control() {
let mut field = field("Modelo", div().w_10());
assert_eq!(field.style().direction, Some(crate::view::style::Direction::Column));
assert_eq!(field.style().gap_y, Some(metrics::TEXT_GAP));
}
}