shelly-liveview 0.2.0

Core runtime primitives for Shelly LiveView.
Documentation
use crate::{Context, Event, Html, LiveResult};
use std::fmt;

/// Stable server-owned identifier for a LiveComponent.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ComponentId(String);

impl ComponentId {
    pub fn new(id: impl Into<String>) -> Self {
        Self(id.into())
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for ComponentId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0)
    }
}

impl From<&str> for ComponentId {
    fn from(value: &str) -> Self {
        Self::new(value)
    }
}

impl From<String> for ComponentId {
    fn from(value: String) -> Self {
        Self::new(value)
    }
}

/// Render result for one component after a scoped event.
#[derive(Debug, Clone, PartialEq)]
pub struct ComponentRender {
    id: ComponentId,
    html: Html,
}

impl ComponentRender {
    pub fn new(id: impl Into<ComponentId>, html: Html) -> Self {
        Self {
            id: id.into(),
            html,
        }
    }

    pub fn id(&self) -> &ComponentId {
        &self.id
    }

    pub fn html(&self) -> &Html {
        &self.html
    }

    pub fn into_parts(self) -> (ComponentId, Html) {
        (self.id, self.html)
    }
}

/// Trait implemented by server-owned child components.
///
/// Components own a smaller state boundary than a full LiveView. Parent views
/// decide how children are stored and rendered, while the session runtime can
/// patch a component target independently after a scoped event.
pub trait LiveComponent: Send + 'static {
    /// Stable id that maps to the component root DOM node.
    fn id(&self) -> ComponentId;

    /// Called by parent views when they initialize child component state.
    fn mount(&mut self, _ctx: &mut Context) -> LiveResult {
        Ok(())
    }

    /// Called by parent views when a scoped event targets this component.
    fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> LiveResult {
        Ok(())
    }

    /// Render this component root HTML.
    fn render(&self) -> Html;
}