This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
el-ui-retired/crates/el-style/src/stylesheet.rs
T

161 lines
4.7 KiB
Rust

/// StyleSheet — named style rules for components.
///
/// Instead of applying modifiers inline everywhere, you can define named
/// style rules in a StyleSheet and apply them with `.apply("card")`.
/// This is the el-ui equivalent of CSS class names, but type-safe and
/// resolved at compile time.
use std::collections::HashMap;
use crate::modifier::StyleSet;
/// A stylesheet: a named collection of StyleSet rules.
#[derive(Debug, Clone, Default)]
pub struct StyleSheet {
rules: HashMap<String, StyleSet>,
}
impl StyleSheet {
pub fn new() -> Self {
Self::default()
}
/// Define a named style rule.
pub fn define(&mut self, name: impl Into<String>, style: StyleSet) -> &mut Self {
self.rules.insert(name.into(), style);
self
}
/// Look up a named style rule.
/// Returns None if the rule doesn't exist.
pub fn get(&self, name: &str) -> Option<&StyleSet> {
self.rules.get(name)
}
/// Apply a named style to a StyleSet by merging — caller's explicit
/// values win, the stylesheet fills in the rest.
///
/// This is an additive operation: fields that are Some in the named
/// rule overwrite fields that are None in the target.
pub fn apply(&self, name: &str, target: &mut StyleSet) {
if let Some(rule) = self.rules.get(name) {
if target.padding.is_none() {
target.padding = rule.padding;
}
if target.margin.is_none() {
target.margin = rule.margin;
}
if target.background.is_none() {
target.background = rule.background.clone();
}
if target.foreground.is_none() {
target.foreground = rule.foreground.clone();
}
if target.font.is_none() {
target.font = rule.font.clone();
}
if target.radius.is_none() {
target.radius = rule.radius;
}
if target.shadow.is_none() {
target.shadow = rule.shadow;
}
if target.opacity.is_none() {
target.opacity = rule.opacity;
}
if target.border_width.is_none() {
target.border_width = rule.border_width;
}
if target.border_color.is_none() {
target.border_color = rule.border_color.clone();
}
if target.width.is_none() {
target.width = rule.width.clone();
}
if target.height.is_none() {
target.height = rule.height.clone();
}
if target.max_width.is_none() {
target.max_width = rule.max_width.clone();
}
}
}
/// The number of rules defined.
pub fn len(&self) -> usize {
self.rules.len()
}
pub fn is_empty(&self) -> bool {
self.rules.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::Color;
use crate::modifier::StyleSet;
fn card_style() -> StyleSet {
let mut s = StyleSet::default();
s.padding = Some([16; 4]);
s.radius = Some([8; 4]);
s.background = Some(Color::Surface);
s
}
#[test]
fn stylesheet_define_and_get() {
let mut ss = StyleSheet::new();
ss.define("card", card_style());
assert!(ss.get("card").is_some());
}
#[test]
fn stylesheet_missing_rule_returns_none() {
let ss = StyleSheet::new();
assert!(ss.get("nonexistent").is_none());
}
#[test]
fn stylesheet_apply_fills_missing_values() {
let mut ss = StyleSheet::new();
ss.define("card", card_style());
let mut target = StyleSet::default();
ss.apply("card", &mut target);
assert_eq!(target.padding, Some([16; 4]));
assert_eq!(target.background, Some(Color::Surface));
}
#[test]
fn stylesheet_apply_does_not_overwrite_existing() {
let mut ss = StyleSheet::new();
ss.define("card", card_style());
let mut target = StyleSet::default();
target.background = Some(Color::Primary); // already set
ss.apply("card", &mut target);
// Should NOT be overwritten by the stylesheet's Surface
assert_eq!(target.background, Some(Color::Primary));
}
#[test]
fn stylesheet_len() {
let mut ss = StyleSheet::new();
assert_eq!(ss.len(), 0);
ss.define("card", card_style());
assert_eq!(ss.len(), 1);
}
#[test]
fn stylesheet_apply_nonexistent_is_noop() {
let ss = StyleSheet::new();
let mut target = StyleSet::default();
ss.apply("does-not-exist", &mut target); // should not panic
assert!(target.padding.is_none());
}
}