use-layer 0.1.0

Layer and z-order semantics primitives for RustUse UI
Documentation
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]

/// Semantic UI layer roles.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum LayerRole {
    Base,
    Raised,
    Sticky,
    Dropdown,
    Popover,
    Tooltip,
    Modal,
    Toast,
    Overlay,
}

impl LayerRole {
    pub fn index(self) -> LayerIndex {
        match self {
            Self::Base => LayerIndex::new(0),
            Self::Raised => LayerIndex::new(100),
            Self::Sticky => LayerIndex::new(200),
            Self::Dropdown => LayerIndex::new(1_000),
            Self::Popover => LayerIndex::new(1_100),
            Self::Tooltip => LayerIndex::new(1_200),
            Self::Modal => LayerIndex::new(1_300),
            Self::Toast => LayerIndex::new(1_400),
            Self::Overlay => LayerIndex::new(1_500),
        }
    }

    pub fn sits_above(self, other: Self) -> bool {
        self.index() > other.index()
    }
}

/// Numeric z-order index.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct LayerIndex(i32);

impl LayerIndex {
    pub fn new(value: i32) -> Self {
        Self(value)
    }

    pub fn value(self) -> i32 {
        self.0
    }
}

/// Ordered layer stack metadata.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct LayerStack {
    layers: Vec<LayerRole>,
}

impl LayerStack {
    pub fn new(layers: Vec<LayerRole>) -> Self {
        Self { layers }
    }

    pub fn layers(&self) -> &[LayerRole] {
        &self.layers
    }

    pub fn top(&self) -> Option<LayerRole> {
        self.layers
            .iter()
            .copied()
            .max_by_key(|layer| layer.index())
    }

    pub fn is_above(&self, layer: LayerRole, other: LayerRole) -> bool {
        self.layers.contains(&layer) && self.layers.contains(&other) && layer.sits_above(other)
    }
}

#[cfg(test)]
mod tests {
    use super::{LayerIndex, LayerRole, LayerStack};

    #[test]
    fn compares_layer_roles() {
        assert!(LayerRole::Modal.sits_above(LayerRole::Popover));
        assert!(!LayerRole::Base.sits_above(LayerRole::Raised));
        assert_eq!(LayerRole::Tooltip.index().value(), 1_200);
        assert!(LayerIndex::new(20) > LayerIndex::new(10));
    }

    #[test]
    fn finds_top_layer_in_stack() {
        let stack = LayerStack::new(vec![LayerRole::Base, LayerRole::Popover, LayerRole::Modal]);

        assert_eq!(stack.top(), Some(LayerRole::Modal));
        assert!(stack.is_above(LayerRole::Modal, LayerRole::Popover));
        assert!(!stack.is_above(LayerRole::Toast, LayerRole::Modal));
    }
}