pagetop/src/html/assets/favicon.rs
Manuel Cillero d042467f50 Añade soporte para recursos en documentos HTML
- Incluye los recursos favicon, hojas de estilo y scripts JavaScript.
- Se introduce una estructura de contexto que, además de gestionar el
  idioma y el uso de parámetros contextuales, permite administrar estos
  recursos en documentos HTML.
2025-07-20 14:24:19 +02:00

165 lines
6.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::html::{html, Markup, Render};
use crate::AutoDefault;
/// Un **Favicon** es un recurso gráfico que usa el navegador como icono asociado al sitio.
///
/// Es universalmente aceptado para mostrar el icono del sitio (`.ico`, `.png`, `.svg`, …) en
/// pestañas, marcadores o accesos directos.
///
/// Este tipo permite construir de forma fluida las distintas variantes de un *favicon*, ya sea un
/// icono estándar, un icono Apple para la pantalla de inicio, o un icono para Safari con color.
/// También puede aplicar colores al tema o configuraciones específicas para *tiles* de Windows.
///
/// > **Nota**
/// > Los archivos de los iconos deben estar disponibles en el servidor web de la aplicación. Pueden
/// > incluirse en el proyecto utilizando [`include_files`](crate::include_files) y servirse con
/// > [`include_files_service`](crate::include_files_service).
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let favicon = Favicon::new()
/// // Estándar de facto admitido por todos los navegadores.
/// .with_icon("/icons/favicon.ico")
///
/// // Variante del favicon con tamaños explícitos: 32×32 y 16×16.
/// .with_icon_for_sizes("/icons/favicon-32.png", "32x32")
/// .with_icon_for_sizes("/icons/favicon-16.png", "16x16")
///
/// // Icono específico para accesos directos en la pantalla de inicio de iOS.
/// .with_apple_touch_icon("/icons/apple-touch-icon.png", "180x180")
///
/// // Icono vectorial con color dinámico para pestañas ancladas en Safari.
/// .with_mask_icon("/icons/safari-pinned-tab.svg", "#5bbad5")
///
/// // Personaliza la barra superior del navegador en Android Chrome (y soportado en otros).
/// .with_theme_color("#ffffff")
///
/// // Personalizaciones específicas para "tiles" en Windows.
/// .with_ms_tile_color("#da532c")
/// .with_ms_tile_image("/icons/mstile-144x144.png");
/// ```
#[derive(AutoDefault)]
pub struct Favicon(Vec<Markup>);
impl Favicon {
/// Crea un nuevo `Favicon` vacío.
///
/// Equivalente a `Favicon::default()`. Se recomienda iniciar la secuencia de configuración
/// desde aquí.
pub fn new() -> Self {
Favicon::default()
}
// Favicon BUILDER *****************************************************************************
/// Le añade un icono genérico apuntando a `image`. El tipo MIME se infiere automáticamente a
/// partir de la extensión.
pub fn with_icon(self, image: impl Into<String>) -> Self {
self.add_icon_item("icon", image.into(), None, None)
}
/// Le añade un icono genérico con atributo `sizes`, útil para indicar resoluciones específicas.
///
/// El atributo `sizes` informa al navegador de las dimensiones de la imagen para que seleccione
/// el recurso más adecuado. Puede enumerar varias dimensiones separadas por espacios, p.ej.
/// `"16x16 32x32 48x48"` o usar `any` para iconos escalables (SVG).
///
/// No es imprescindible, pero puede mejorar la selección del icono más adecuado.
pub fn with_icon_for_sizes(self, image: impl Into<String>, sizes: impl Into<String>) -> Self {
self.add_icon_item("icon", image.into(), Some(sizes.into()), None)
}
/// Le añade un *Apple Touch Icon*, usado por dispositivos iOS para las pantallas de inicio.
///
/// Se recomienda indicar también el tamaño, p.ej. `"256x256"`.
pub fn with_apple_touch_icon(self, image: impl Into<String>, sizes: impl Into<String>) -> Self {
self.add_icon_item("apple-touch-icon", image.into(), Some(sizes.into()), None)
}
/// Le añade un icono para el navegador Safari, con un color dinámico.
///
/// El atributo `color` lo usa Safari para colorear el trazado SVG cuando el icono se muestra en
/// modo *Pinned Tab*. Aunque Safari 12+ acepta *favicons normales*, este método garantiza
/// compatibilidad con versiones anteriores.
pub fn with_mask_icon(self, image: impl Into<String>, color: impl Into<String>) -> Self {
self.add_icon_item("mask-icon", image.into(), None, Some(color.into()))
}
/// Define el color del tema (`<meta name="theme-color">`).
///
/// Lo usan algunos navegadores para colorear la barra de direcciones o interfaces.
pub fn with_theme_color(mut self, color: impl Into<String>) -> Self {
self.0.push(html! {
meta name="theme-color" content=(color.into());
});
self
}
/// Define el color del *tile* en Windows (`<meta name="msapplication-TileColor">`).
pub fn with_ms_tile_color(mut self, color: impl Into<String>) -> Self {
self.0.push(html! {
meta name="msapplication-TileColor" content=(color.into());
});
self
}
/// Define la imagen del *tile* en Windows (`<meta name="msapplication-TileImage">`).
pub fn with_ms_tile_image(mut self, image: impl Into<String>) -> Self {
self.0.push(html! {
meta name="msapplication-TileImage" content=(image.into());
});
self
}
// Función interna que centraliza la creación de las etiquetas `<link>`.
//
// - `icon_rel`: indica el tipo de recurso (`"icon"`, `"apple-touch-icon"`, etc.).
// - `icon_source`: URL del recurso.
// - `icon_sizes`: tamaños opcionales.
// - `icon_color`: color opcional (solo relevante para `mask-icon`).
//
// También infiere automáticamente el tipo MIME (`type`) según la extensión del archivo.
fn add_icon_item(
mut self,
icon_rel: &str,
icon_source: String,
icon_sizes: Option<String>,
icon_color: Option<String>,
) -> Self {
let icon_type = match icon_source.rfind('.') {
Some(i) => match icon_source[i..].to_owned().to_lowercase().as_str() {
".avif" => Some("image/avif"),
".gif" => Some("image/gif"),
".ico" => Some("image/x-icon"),
".jpg" | ".jpeg" => Some("image/jpeg"),
".png" => Some("image/png"),
".svg" => Some("image/svg+xml"),
".webp" => Some("image/webp"),
_ => None,
},
_ => None,
};
self.0.push(html! {
link
rel=(icon_rel)
type=[(icon_type)]
sizes=[(icon_sizes)]
color=[(icon_color)]
href=(icon_source);
});
self
}
}
impl Render for Favicon {
fn render(&self) -> Markup {
html! {
@for item in &self.0 {
(item)
}
}
}
}