tailwind_css_fixes/modules/effects/mask/mask_image/
mod.rs

1use super::*;
2
3// This new file would live alongside your other rule files.
4// E.g., `src/rules/mask_image.rs`
5
6#[derive(Clone, Debug)]
7pub struct TailwindMaskImage {
8    // The type of image/gradient for the mask
9    kind: MaskImageKind,
10    // The value (e.g., direction, angle).
11    value: Option<UnitValue>,
12    // Flag for negative angles
13    is_negative: bool,
14}
15
16#[derive(Clone, Debug, PartialEq)]
17enum MaskImageKind {
18    None,
19    Linear,
20    Radial,
21    Conic,
22    // Add Url, RepeatingLinear, etc. as needed in the future
23}
24
25impl Display for TailwindMaskImage {
26    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
27        if self.is_negative {
28            write!(f, "-")?;
29        }
30
31        match (&self.kind, &self.value) {
32            (MaskImageKind::None, _) => write!(f, "mask-none"),
33            (MaskImageKind::Linear, Some(val)) => write!(f, "mask-linear-{}", val),
34            (MaskImageKind::Radial, Some(val)) => write!(f, "mask-radial-{}", val),
35            (MaskImageKind::Conic, Some(val)) => write!(f, "mask-conic-{}", val),
36            (MaskImageKind::Linear, None) => write!(f, "mask-linear"),
37            (MaskImageKind::Radial, None) => write!(f, "mask-radial"),
38            (MaskImageKind::Conic, None) => write!(f, "mask-conic"),
39            _ => Ok(()),
40        }
41    }
42}
43
44impl TailwindInstance for TailwindMaskImage {
45    fn attributes(&self, _: &TailwindBuilder) -> CssAttributes {
46        let (prefix, property) = ("--tw-mask", "mask-image");
47
48        match &self.kind {
49            MaskImageKind::None => css_attributes! { property => "none" },
50            MaskImageKind::Linear | MaskImageKind::Radial | MaskImageKind::Conic => {
51                let gradient_type = match self.kind {
52                    MaskImageKind::Linear => "linear-gradient",
53                    MaskImageKind::Radial => "radial-gradient",
54                    MaskImageKind::Conic => "conic-gradient",
55                    _ => unreachable!(),
56                };
57
58                let direction_or_angle = match self.value.as_ref() {
59                    None => "".to_string(), // Default direction/shape is often empty
60                    Some(UnitValue::Keyword(k)) if k.starts_with("to-") => {
61                        // ... logic to convert "to-t" to "to top" ...
62                        // This can be copied from your TailwindBackgroundImage impl
63                        k.replace("to-", "to ").replace("-", " ")
64                    }
65                    Some(value) => {
66                        let inner_value = match value {
67                            UnitValue::Arbitrary(a) => a.get_properties(),
68                            _ => value.to_string(),
69                        };
70                        if self.is_negative {
71                            format!("calc({} * -1)", inner_value)
72                        } else {
73                            inner_value
74                        }
75                    }
76                };
77
78                // The composable gradient that uses CSS variables
79                let image = format!("{}({})", gradient_type, format!("{}, var({}-stops))", direction_or_angle, prefix));
80
81                css_attributes! {
82                    format!("{}-position", prefix) => direction_or_angle,
83                    property => image
84                }
85            }
86        }
87    }
88}
89
90impl TailwindMaskImage {
91    /// Parses `mask-none`, `mask-linear`, `mask-radial`, `mask-[...gradient...]`
92    pub fn parse(pattern: &[&str], arbitrary: &TailwindArbitrary, neg: Negative) -> Result<Self> {
93        let (kind, rest) = match pattern {
94            ["none"] => (MaskImageKind::None, &pattern[1..]),
95            ["linear", rest @ ..] => (MaskImageKind::Linear, rest),
96            ["radial", rest @ ..] => (MaskImageKind::Radial, rest),
97            ["conic", rest @ ..] => (MaskImageKind::Conic, rest),
98            // Handle arbitrary gradients
99            [] if arbitrary.as_str().contains("gradient") => {
100                // For arbitrary, we can treat it like a simple linear gradient for parsing purposes
101                (MaskImageKind::Linear, pattern)
102            }
103            _ => return syntax_error!("Unknown mask-image pattern: {}", pattern.join("-")),
104        };
105
106        let value = match kind {
107            MaskImageKind::None => None,
108            _ => { // All gradient types
109                if arbitrary.is_some() {
110                    // For mask-[radial-gradient(...)], the full value is in `arbitrary`
111                     let grad_content = arbitrary.as_str()
112                        .replace('_', " ")
113                        .trim_start_matches("linear-gradient(")
114                        .trim_start_matches("radial-gradient(")
115                        .trim_start_matches("conic-gradient(")
116                        .trim_end_matches(')')
117                        .to_string();
118                    Some(UnitValue::Keyword(grad_content))
119                } else if rest.is_empty() {
120                    None // e.g., `mask-linear`
121                } else {
122                    Some(UnitValue::negative_parser("mask-image", |_| false, false, false, false)(rest, arbitrary, neg)?)
123                }
124            }
125        };
126
127        Ok(Self { kind, value, is_negative: neg.0 })
128    }
129}