diff --git a/src/core/component.rs b/src/core/component.rs index e5858cb..17b9b73 100644 --- a/src/core/component.rs +++ b/src/core/component.rs @@ -7,6 +7,3 @@ mod children; pub use children::Children; pub use children::{Child, ChildOp}; pub use children::{Typed, TypedOp}; - -mod optional; -pub use optional::TypedOpt; diff --git a/src/core/component/children.rs b/src/core/component/children.rs index cb112e1..2b16f06 100644 --- a/src/core/component/children.rs +++ b/src/core/component/children.rs @@ -1,6 +1,6 @@ use crate::core::component::Component; use crate::html::{html, Context, Markup}; -use crate::{builder_fn, UniqueId}; +use crate::{builder_fn, AutoDefault, UniqueId}; use parking_lot::RwLock; @@ -11,76 +11,105 @@ use std::vec::IntoIter; /// /// Esta estructura permite manipular y renderizar un componente que implemente [`Component`], y /// habilita acceso concurrente mediante [`Arc>`]. -#[derive(Clone)] -pub struct Child(Arc>); +#[derive(AutoDefault, Clone)] +pub struct Child(Option>>); impl Child { /// Crea un nuevo `Child` a partir de un componente. pub fn with(component: impl Component) -> Self { - Child(Arc::new(RwLock::new(component))) + Child(Some(Arc::new(RwLock::new(component)))) + } + + // Child BUILDER ******************************************************************************* + + /// Establece un componente nuevo, o lo vacía. + /// + /// Si se proporciona `Some(component)`, se encapsula como [`Child`]; y si es `None`, se limpia. + #[builder_fn] + pub fn with_component(mut self, component: Option) -> Self { + if let Some(c) = component { + self.0 = Some(Arc::new(RwLock::new(c))); + } else { + self.0 = None; + } + self } // Child GETTERS ******************************************************************************* - /// Devuelve el identificador del componente, si está definido. + /// Devuelve el identificador del componente, si existe y está definido. + #[inline] pub fn id(&self) -> Option { - self.0.read().id() + self.0.as_ref().and_then(|c| c.read().id()) } // Child RENDER ******************************************************************************** /// Renderiza el componente con el contexto proporcionado. pub fn render(&self, cx: &mut Context) -> Markup { - self.0.write().render(cx) + self.0.as_ref().map_or(html! {}, |c| c.write().render(cx)) } // Child HELPERS ******************************************************************************* - // Devuelve el [`UniqueId`] del tipo del componente. - fn type_id(&self) -> UniqueId { - self.0.read().type_id() + // Devuelve el [`UniqueId`] del tipo del componente, si existe. + #[inline] + fn type_id(&self) -> Option { + self.0.as_ref().map(|c| c.read().type_id()) } } // ************************************************************************************************* -/// Variante tipada de [`Child`] para evitar conversiones durante el uso. +/// Variante tipada de [`Child`] para evitar conversiones de tipo durante el uso. /// /// Esta estructura permite manipular y renderizar un componente concreto que implemente /// [`Component`], y habilita acceso concurrente mediante [`Arc>`]. -pub struct Typed(Arc>); - -impl Clone for Typed { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} +#[derive(AutoDefault, Clone)] +pub struct Typed(Option>>); impl Typed { /// Crea un nuevo `Typed` a partir de un componente. pub fn with(component: C) -> Self { - Typed(Arc::new(RwLock::new(component))) + Typed(Some(Arc::new(RwLock::new(component)))) + } + + // Typed BUILDER ******************************************************************************* + + /// Establece un componente nuevo, o lo vacía. + /// + /// Si se proporciona `Some(component)`, se encapsula como [`Typed`]; y si es `None`, se limpia. + #[builder_fn] + pub fn with_component(mut self, component: Option) -> Self { + self.0 = component.map(|c| Arc::new(RwLock::new(c))); + self } // Typed GETTERS ******************************************************************************* - /// Devuelve el identificador del componente, si está definido. + /// Devuelve el identificador del componente, si existe y está definido. + #[inline] pub fn id(&self) -> Option { - self.0.read().id() + self.0.as_ref().and_then(|c| c.read().id()) } // Typed RENDER ******************************************************************************** /// Renderiza el componente con el contexto proporcionado. pub fn render(&self, cx: &mut Context) -> Markup { - self.0.write().render(cx) + self.0.as_ref().map_or(html! {}, |c| c.write().render(cx)) } // Typed HELPERS ******************************************************************************* // Convierte el componente tipado en un [`Child`]. + #[inline] fn into_child(self) -> Child { - Child(self.0.clone()) + if let Some(c) = &self.0 { + Child(Some(c.clone())) + } else { + Child(None) + } } } @@ -201,7 +230,7 @@ impl Children { /// Devuelve un iterador sobre los componentes hijo con el identificador de tipo ([`UniqueId`]) /// indicado. pub fn iter_by_type_id(&self, type_id: UniqueId) -> impl Iterator { - self.0.iter().filter(move |&c| c.type_id() == type_id) + self.0.iter().filter(move |&c| c.type_id() == Some(type_id)) } // Children RENDER ***************************************************************************** diff --git a/src/core/component/optional.rs b/src/core/component/optional.rs deleted file mode 100644 index c13b833..0000000 --- a/src/core/component/optional.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::core::component::{Component, Typed}; -use crate::html::{html, Context, Markup}; -use crate::{builder_fn, AutoDefault}; - -/// Contenedor **opcional** para un componente [`Typed`]. -/// -/// Un `TypedOpt` actúa como un contenedor para incluir o no un subcomponente tipado. Internamente -/// encapsula `Option>`, pero ofrece una API más sencilla para construir estructuras -/// jerárquicas o contenidas de componentes. -/// -/// # Ejemplo -/// -/// ```rust -/// use pagetop::prelude::*; -/// -/// let icon = Icon::default(); -/// let icon = TypedOpt::new(icon); -/// assert!(icon.get().is_some()); -/// ``` -#[derive(AutoDefault)] -pub struct TypedOpt(Option>); - -impl TypedOpt { - /// Crea un nuevo [`TypedOpt`]. - /// - /// El componente se envuelve automáticamente en un [`Typed`] y se almacena. - pub fn new(component: C) -> Self { - TypedOpt(Some(Typed::with(component))) - } - - // TypedOpt BUILDER **************************************************************************** - - /// Establece un componente nuevo, o lo vacía. - /// - /// Si se proporciona `Some(component)`, se guarda como [`Typed`]; y si es `None`, se limpia. - #[builder_fn] - pub fn with_component(mut self, component: Option) -> Self { - self.0 = component.map(Typed::with); - self - } - - // TypedOpt GETTERS **************************************************************************** - - /// Devuelve un clon (incrementa el contador `Arc`) de [`Typed`], si existe. - pub fn get(&self) -> Option> { - self.0.clone() - } - - // TypedOpt RENDER ***************************************************************************** - - /// Renderiza el componente, si existe. - pub fn render(&self, cx: &mut Context) -> Markup { - if let Some(component) = &self.0 { - component.render(cx) - } else { - html! {} - } - } -} diff --git a/src/html.rs b/src/html.rs index 89d19d4..4a965ca 100644 --- a/src/html.rs +++ b/src/html.rs @@ -50,14 +50,13 @@ pub type OptionClasses = AttrClasses; use crate::{core, AutoDefault}; -/// **Obsoleto desde la versión 0.4.0**: usar [`TypedOpt`](crate::core::component::TypedOpt) en su -/// lugar. +/// **Obsoleto desde la versión 0.4.0**: usar [`Typed`](crate::core::component::Typed) en su lugar. #[deprecated( since = "0.4.0", - note = "Use `pagetop::core::component::TypedOpt` instead" + note = "Use `pagetop::core::component::Typed` instead" )] #[allow(type_alias_bounds)] -pub type OptionComponent = core::component::TypedOpt; +pub type OptionComponent = core::component::Typed; /// Prepara contenido HTML para su conversión a [`Markup`]. ///