use crate::core::{Rgba, px};
use crate::view::interactivity::ClickHandler;
use crate::view::prelude::*;
use crate::widgets::icons::{IconName, icon};
pub fn modal(content: impl IntoElement, on_dismiss: Option<ClickHandler>) -> Div {
div()
.absolute()
.inset_0()
.flex_col()
.items_center()
.occlude()
.bg(Rgba::new(0.0, 0.0, 0.0, 0.45))
.when_some(on_dismiss, |backdrop, handler| backdrop.on_mouse_down(move |event, cx| handler(event, cx)))
.child(div().mt(px(88.0)).child(content))
}
pub fn toast(message: impl Into<String>, is_error: bool) -> Div {
toast_with(wrap_words(&message.into(), TOAST_LINE_CHARS), is_error)
}
const TOAST_LINE_CHARS: usize = 48;
pub fn wrap_words(text: &str, width: usize) -> String {
let mut lines: Vec<String> = Vec::new();
for paragraph in text.split('\n') {
let mut line = String::new();
for word in paragraph.split_whitespace() {
let needed = if line.is_empty() { 0 } else { 1 } + word.chars().count();
if !line.is_empty() && line.chars().count() + needed > width {
lines.push(std::mem::take(&mut line));
}
if !line.is_empty() {
line.push(' ');
}
line.push_str(word);
}
lines.push(line);
}
lines.join("\n")
}
pub fn toast_with(content: impl IntoElement, is_error: bool) -> Div {
div().absolute().inset_0().flex_col().items_end().justify_end().p_6().child(
div()
.occlude()
.flex_row()
.items_center()
.gap_2()
.max_w(px(420.0))
.px_3()
.py_2()
.rounded_lg()
.border_1()
.border_color(if is_error { Token::Danger } else { Token::Border })
.bg(Token::ElevatedSurface)
.shadow_lg()
.text_sm()
.whitespace_normal()
.child(icon(if is_error { IconName::CircleAlert } else { IconName::Info }).text_color(if is_error {
Token::Danger
} else {
Token::Accent
}))
.child(content),
)
}
#[cfg(test)]
mod toast_tests {
use super::wrap_words;
#[test]
fn el_texto_largo_se_parte_en_espacios() {
let wrapped = wrap_words("ajustes de IA: configura el ID exacto del modelo de tu cuenta o servidor", 30);
assert_eq!(wrapped, "ajustes de IA: configura el ID\nexacto del modelo de tu cuenta\no servidor");
assert!(wrapped.lines().all(|line| line.chars().count() <= 30));
}
#[test]
fn respeta_saltos_previos_y_palabras_largas() {
assert_eq!(wrap_words("a\nb", 10), "a\nb");
assert_eq!(wrap_words("https://ejemplo.invalid/ruta muy larga", 10), "https://ejemplo.invalid/ruta\nmuy larga");
assert_eq!(wrap_words("corto", 48), "corto");
}
}