orbital-base-components 0.1.1

Headless base primitives for Orbital — structure, semantics, and accessibility
Documentation
use leptos::{context::Provider, prelude::*};

use super::{SkeletonInjection, SkeletonItemShape, SkeletonItemSize};

#[component]
pub fn BaseSkeleton(
    #[prop(optional, into)] class: MaybeProp<String>,
    #[prop(optional, into)] size: Option<Signal<SkeletonItemSize>>,
    #[prop(optional, into)] shape: Option<Signal<SkeletonItemShape>>,
    children: Children,
) -> impl IntoView {
    view! {
        <div
            class=move || {
                let mut parts = vec!["orbital-skeleton".to_string()];
                if let Some(extra) = class.get() {
                    if !extra.is_empty() {
                        parts.push(extra);
                    }
                }
                parts.join(" ")
            }
            role="progressbar"
            aria-busy="true"
        >
            <Provider value=SkeletonInjection { size, shape }>{children()}</Provider>
        </div>
    }
}

#[component]
pub fn BaseSkeletonItem(
    #[prop(optional, into)] class: MaybeProp<String>,
    #[prop(optional, into)] width: MaybeProp<String>,
    #[prop(optional, into)] height: MaybeProp<String>,
    #[prop(optional, into)] size: Option<Signal<SkeletonItemSize>>,
    #[prop(optional, into)] shape: Option<Signal<SkeletonItemShape>>,
) -> impl IntoView {
    let context = SkeletonInjection::use_context();

    let modifier_class = Signal::derive(move || {
        let resolved_size = size
            .map(|signal| signal.get())
            .or_else(|| {
                context
                    .as_ref()
                    .and_then(|ctx| ctx.size.map(|signal| signal.get()))
            })
            .unwrap_or_default();

        let resolved_shape = shape
            .map(|signal| signal.get())
            .or_else(|| {
                context
                    .as_ref()
                    .and_then(|ctx| ctx.shape.map(|signal| signal.get()))
            })
            .unwrap_or_default();

        format!(
            "orbital-skeleton-item--{} orbital-skeleton-item--size-{}",
            resolved_shape.as_str(),
            resolved_size.px()
        )
    });

    view! {
        <div
            class=move || {
                let mut parts = vec![
                    "orbital-skeleton-item".to_string(),
                    modifier_class.get(),
                ];
                if let Some(extra) = class.get() {
                    if !extra.is_empty() {
                        parts.push(extra);
                    }
                }
                parts.join(" ")
            }
            style=move || {
                let mut parts = Vec::new();
                if let Some(w) = width.get() {
                    parts.push(format!("width: {w}"));
                }
                if let Some(h) = height.get() {
                    parts.push(format!("height: {h}"));
                }
                parts.join("; ")
            }
            aria-hidden="true"
        ></div>
    }
}