tailwind_rs_core/utilities/effects/
blend_modes.rs

1//! Blend mode utilities for tailwind-rs
2//!
3//! This module provides utilities for mix blend mode and background blend mode effects.
4
5use crate::classes::ClassBuilder;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9/// Mix blend mode values
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum MixBlendMode {
12    /// Normal blend
13    Normal,
14    /// Multiply blend
15    Multiply,
16    /// Screen blend
17    Screen,
18    /// Overlay blend
19    Overlay,
20    /// Darken blend
21    Darken,
22    /// Lighten blend
23    Lighten,
24    /// Color dodge blend
25    ColorDodge,
26    /// Color burn blend
27    ColorBurn,
28    /// Hard light blend
29    HardLight,
30    /// Soft light blend
31    SoftLight,
32    /// Difference blend
33    Difference,
34    /// Exclusion blend
35    Exclusion,
36    /// Hue blend
37    Hue,
38    /// Saturation blend
39    Saturation,
40    /// Color blend
41    Color,
42    /// Luminosity blend
43    Luminosity,
44}
45
46/// Background blend mode values
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
48pub enum BackgroundBlendMode {
49    /// Normal blend
50    Normal,
51    /// Multiply blend
52    Multiply,
53    /// Screen blend
54    Screen,
55    /// Overlay blend
56    Overlay,
57    /// Darken blend
58    Darken,
59    /// Lighten blend
60    Lighten,
61    /// Color dodge blend
62    ColorDodge,
63    /// Color burn blend
64    ColorBurn,
65    /// Hard light blend
66    HardLight,
67    /// Soft light blend
68    SoftLight,
69    /// Difference blend
70    Difference,
71    /// Exclusion blend
72    Exclusion,
73    /// Hue blend
74    Hue,
75    /// Saturation blend
76    Saturation,
77    /// Color blend
78    Color,
79    /// Luminosity blend
80    Luminosity,
81}
82
83impl fmt::Display for MixBlendMode {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            MixBlendMode::Normal => write!(f, "mix-blend-normal"),
87            MixBlendMode::Multiply => write!(f, "mix-blend-multiply"),
88            MixBlendMode::Screen => write!(f, "mix-blend-screen"),
89            MixBlendMode::Overlay => write!(f, "mix-blend-overlay"),
90            MixBlendMode::Darken => write!(f, "mix-blend-darken"),
91            MixBlendMode::Lighten => write!(f, "mix-blend-lighten"),
92            MixBlendMode::ColorDodge => write!(f, "mix-blend-color-dodge"),
93            MixBlendMode::ColorBurn => write!(f, "mix-blend-color-burn"),
94            MixBlendMode::HardLight => write!(f, "mix-blend-hard-light"),
95            MixBlendMode::SoftLight => write!(f, "mix-blend-soft-light"),
96            MixBlendMode::Difference => write!(f, "mix-blend-difference"),
97            MixBlendMode::Exclusion => write!(f, "mix-blend-exclusion"),
98            MixBlendMode::Hue => write!(f, "mix-blend-hue"),
99            MixBlendMode::Saturation => write!(f, "mix-blend-saturation"),
100            MixBlendMode::Color => write!(f, "mix-blend-color"),
101            MixBlendMode::Luminosity => write!(f, "mix-blend-luminosity"),
102        }
103    }
104}
105
106impl fmt::Display for BackgroundBlendMode {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            BackgroundBlendMode::Normal => write!(f, "bg-blend-normal"),
110            BackgroundBlendMode::Multiply => write!(f, "bg-blend-multiply"),
111            BackgroundBlendMode::Screen => write!(f, "bg-blend-screen"),
112            BackgroundBlendMode::Overlay => write!(f, "bg-blend-overlay"),
113            BackgroundBlendMode::Darken => write!(f, "bg-blend-darken"),
114            BackgroundBlendMode::Lighten => write!(f, "bg-blend-lighten"),
115            BackgroundBlendMode::ColorDodge => write!(f, "bg-blend-color-dodge"),
116            BackgroundBlendMode::ColorBurn => write!(f, "bg-blend-color-burn"),
117            BackgroundBlendMode::HardLight => write!(f, "bg-blend-hard-light"),
118            BackgroundBlendMode::SoftLight => write!(f, "bg-blend-soft-light"),
119            BackgroundBlendMode::Difference => write!(f, "bg-blend-difference"),
120            BackgroundBlendMode::Exclusion => write!(f, "bg-blend-exclusion"),
121            BackgroundBlendMode::Hue => write!(f, "bg-blend-hue"),
122            BackgroundBlendMode::Saturation => write!(f, "bg-blend-saturation"),
123            BackgroundBlendMode::Color => write!(f, "bg-blend-color"),
124            BackgroundBlendMode::Luminosity => write!(f, "bg-blend-luminosity"),
125        }
126    }
127}
128
129/// Trait for adding mix blend mode utilities to a class builder
130pub trait MixBlendModeUtilities {
131    fn mix_blend_mode(self, mode: MixBlendMode) -> Self;
132}
133
134/// Trait for adding background blend mode utilities to a class builder
135pub trait BackgroundBlendModeUtilities {
136    fn background_blend_mode(self, mode: BackgroundBlendMode) -> Self;
137}
138
139impl MixBlendModeUtilities for ClassBuilder {
140    fn mix_blend_mode(self, mode: MixBlendMode) -> Self {
141        self.class(mode.to_string())
142    }
143}
144
145impl BackgroundBlendModeUtilities for ClassBuilder {
146    fn background_blend_mode(self, mode: BackgroundBlendMode) -> Self {
147        self.class(mode.to_string())
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_mix_blend_mode_display() {
157        assert_eq!(MixBlendMode::Normal.to_string(), "mix-blend-normal");
158        assert_eq!(MixBlendMode::Multiply.to_string(), "mix-blend-multiply");
159        assert_eq!(MixBlendMode::Screen.to_string(), "mix-blend-screen");
160        assert_eq!(MixBlendMode::Overlay.to_string(), "mix-blend-overlay");
161        assert_eq!(MixBlendMode::Darken.to_string(), "mix-blend-darken");
162        assert_eq!(MixBlendMode::Lighten.to_string(), "mix-blend-lighten");
163        assert_eq!(MixBlendMode::ColorDodge.to_string(), "mix-blend-color-dodge");
164        assert_eq!(MixBlendMode::ColorBurn.to_string(), "mix-blend-color-burn");
165        assert_eq!(MixBlendMode::HardLight.to_string(), "mix-blend-hard-light");
166        assert_eq!(MixBlendMode::SoftLight.to_string(), "mix-blend-soft-light");
167        assert_eq!(MixBlendMode::Difference.to_string(), "mix-blend-difference");
168        assert_eq!(MixBlendMode::Exclusion.to_string(), "mix-blend-exclusion");
169        assert_eq!(MixBlendMode::Hue.to_string(), "mix-blend-hue");
170        assert_eq!(MixBlendMode::Saturation.to_string(), "mix-blend-saturation");
171        assert_eq!(MixBlendMode::Color.to_string(), "mix-blend-color");
172        assert_eq!(MixBlendMode::Luminosity.to_string(), "mix-blend-luminosity");
173    }
174
175    #[test]
176    fn test_background_blend_mode_display() {
177        assert_eq!(BackgroundBlendMode::Normal.to_string(), "bg-blend-normal");
178        assert_eq!(BackgroundBlendMode::Multiply.to_string(), "bg-blend-multiply");
179        assert_eq!(BackgroundBlendMode::Screen.to_string(), "bg-blend-screen");
180        assert_eq!(BackgroundBlendMode::Overlay.to_string(), "bg-blend-overlay");
181        assert_eq!(BackgroundBlendMode::Darken.to_string(), "bg-blend-darken");
182        assert_eq!(BackgroundBlendMode::Lighten.to_string(), "bg-blend-lighten");
183        assert_eq!(BackgroundBlendMode::ColorDodge.to_string(), "bg-blend-color-dodge");
184        assert_eq!(BackgroundBlendMode::ColorBurn.to_string(), "bg-blend-color-burn");
185        assert_eq!(BackgroundBlendMode::HardLight.to_string(), "bg-blend-hard-light");
186        assert_eq!(BackgroundBlendMode::SoftLight.to_string(), "bg-blend-soft-light");
187        assert_eq!(BackgroundBlendMode::Difference.to_string(), "bg-blend-difference");
188        assert_eq!(BackgroundBlendMode::Exclusion.to_string(), "bg-blend-exclusion");
189        assert_eq!(BackgroundBlendMode::Hue.to_string(), "bg-blend-hue");
190        assert_eq!(BackgroundBlendMode::Saturation.to_string(), "bg-blend-saturation");
191        assert_eq!(BackgroundBlendMode::Color.to_string(), "bg-blend-color");
192        assert_eq!(BackgroundBlendMode::Luminosity.to_string(), "bg-blend-luminosity");
193    }
194
195    #[test]
196    fn test_mix_blend_mode_utilities() {
197        let classes = ClassBuilder::new()
198            .mix_blend_mode(MixBlendMode::Multiply)
199            .build();
200        
201        assert!(classes.to_css_classes().contains("mix-blend-multiply"));
202    }
203
204    #[test]
205    fn test_background_blend_mode_utilities() {
206        let classes = ClassBuilder::new()
207            .background_blend_mode(BackgroundBlendMode::Screen)
208            .build();
209        
210        assert!(classes.to_css_classes().contains("bg-blend-screen"));
211    }
212
213    #[test]
214    fn test_blend_mode_serialization() {
215        let mix_blend_mode = MixBlendMode::Overlay;
216        let serialized = serde_json::to_string(&mix_blend_mode).unwrap();
217        let deserialized: MixBlendMode = serde_json::from_str(&serialized).unwrap();
218        assert_eq!(mix_blend_mode, deserialized);
219
220        let background_blend_mode = BackgroundBlendMode::Overlay;
221        let serialized = serde_json::to_string(&background_blend_mode).unwrap();
222        let deserialized: BackgroundBlendMode = serde_json::from_str(&serialized).unwrap();
223        assert_eq!(background_blend_mode, deserialized);
224    }
225
226    #[test]
227    fn test_blend_mode_equality_and_hash() {
228        let mix_blend_mode1 = MixBlendMode::Multiply;
229        let mix_blend_mode2 = MixBlendMode::Multiply;
230        let mix_blend_mode3 = MixBlendMode::Screen;
231        
232        assert_eq!(mix_blend_mode1, mix_blend_mode2);
233        assert_ne!(mix_blend_mode1, mix_blend_mode3);
234        
235        let background_blend_mode1 = BackgroundBlendMode::Multiply;
236        let background_blend_mode2 = BackgroundBlendMode::Multiply;
237        let background_blend_mode3 = BackgroundBlendMode::Screen;
238        
239        assert_eq!(background_blend_mode1, background_blend_mode2);
240        assert_ne!(background_blend_mode1, background_blend_mode3);
241        
242        // Test that equal effects have the same hash
243        use std::collections::hash_map::DefaultHasher;
244        use std::hash::{Hash, Hasher};
245        
246        let mut hasher1 = DefaultHasher::new();
247        let mut hasher2 = DefaultHasher::new();
248        mix_blend_mode1.hash(&mut hasher1);
249        mix_blend_mode2.hash(&mut hasher2);
250        assert_eq!(hasher1.finish(), hasher2.finish());
251        
252        let mut hasher1 = DefaultHasher::new();
253        let mut hasher2 = DefaultHasher::new();
254        background_blend_mode1.hash(&mut hasher1);
255        background_blend_mode2.hash(&mut hasher2);
256        assert_eq!(hasher1.finish(), hasher2.finish());
257    }
258}