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); 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) -> 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, sizes: impl Into) -> 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, sizes: impl Into) -> 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, color: impl Into) -> Self { self.add_icon_item("mask-icon", image.into(), None, Some(color.into())) } /// Define el color del tema (``). /// /// Lo usan algunos navegadores para colorear la barra de direcciones o interfaces. pub fn with_theme_color(mut self, color: impl Into) -> Self { self.0.push(html! { meta name="theme-color" content=(color.into()); }); self } /// Define el color del *tile* en Windows (``). pub fn with_ms_tile_color(mut self, color: impl Into) -> Self { self.0.push(html! { meta name="msapplication-TileColor" content=(color.into()); }); self } /// Define la imagen del *tile* en Windows (``). pub fn with_ms_tile_image(mut self, image: impl Into) -> Self { self.0.push(html! { meta name="msapplication-TileImage" content=(image.into()); }); self } // Función interna que centraliza la creación de las etiquetas ``. // // - `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, icon_color: Option, ) -> 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) } } } }