1use css_parse::Box;
2
3use super::prelude::*;
4use crate::{CalcableValue, LengthPercentage};
5
6#[node]
11#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
13#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
14#[derive(csskit_derives::NodeWithMetadata)]
15pub enum Gradient<'a> {
16 #[atom(CssAtomSet::LinearGradient)]
17 LinearGradientFunction(Box<'a, LinearGradientFunction<'a>>),
18 #[atom(CssAtomSet::RepeatingLinearGradient)]
19 RepeatingLinearGradientFunction(Box<'a, RepeatingLinearGradientFunction<'a>>),
20 #[atom(CssAtomSet::RadialGradient)]
21 RadialGradientFunction(Box<'a, RadialGradientFunction<'a>>),
22 #[atom(CssAtomSet::RepeatingRadialGradient)]
23 RepeatingRadialGradientFunction(Box<'a, RepeatingRadialGradientFunction<'a>>),
24}
25
26#[node]
33#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
35#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
36#[derive(csskit_derives::NodeWithMetadata)]
37pub struct LinearGradientFunction<'a> {
38 #[atom(CssAtomSet::LinearGradient)]
39 pub name: T![Function],
40 pub params: LinearGradientFunctionParams<'a>,
41 #[semantic_eq(skip)]
42 pub close: T![')'],
43}
44
45#[node]
46#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
48#[derive(csskit_derives::NodeWithMetadata)]
49pub struct LinearGradientFunctionParams<'a>(
50 Option<LinearDirection<'a>>,
51 #[semantic_eq(skip)] Option<T![,]>,
52 CommaSeparated<'a, ColorStopOrHint<'a>>,
53);
54
55#[node]
62#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
64#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
65#[derive(csskit_derives::NodeWithMetadata)]
66pub struct RepeatingLinearGradientFunction<'a> {
67 #[atom(CssAtomSet::RepeatingLinearGradient)]
68 pub name: T![Function],
69 pub params: RepeatingLinearGradientFunctionParams<'a>,
70 #[semantic_eq(skip)]
71 pub close: T![')'],
72}
73
74#[node]
75#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
77#[derive(csskit_derives::NodeWithMetadata)]
78pub struct RepeatingLinearGradientFunctionParams<'a>(
79 Option<LinearDirection<'a>>,
80 #[semantic_eq(skip)] Option<T![,]>,
81 CommaSeparated<'a, ColorStopOrHint<'a>>,
82);
83
84#[node]
93#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
95#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
96#[derive(csskit_derives::NodeWithMetadata)]
97pub struct RadialGradientFunction<'a> {
98 #[atom(CssAtomSet::RadialGradient)]
99 pub name: T![Function],
100 pub params: RadialGradientFunctionParams<'a>,
101 #[semantic_eq(skip)]
102 pub close: T![')'],
103}
104
105#[node]
106#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
108#[derive(csskit_derives::NodeWithMetadata)]
109pub struct RadialGradientFunctionParams<'a>(
110 Option<RadialSize<'a>>,
111 Option<RadialShape>,
112 Option<T![Ident]>,
113 Option<Position<'a>>,
114 #[semantic_eq(skip)] Option<T![,]>,
115 CommaSeparated<'a, ColorStopOrHint<'a>>,
116);
117
118#[node]
127#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
129#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
130#[derive(csskit_derives::NodeWithMetadata)]
131pub struct RepeatingRadialGradientFunction<'a> {
132 #[atom(CssAtomSet::RepeatingRadialGradient)]
133 pub name: T![Function],
134 pub params: RepeatingRadialGradientFunctionParams<'a>,
135 #[semantic_eq(skip)]
136 pub close: T![')'],
137}
138
139#[node]
140#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
142#[derive(csskit_derives::NodeWithMetadata)]
143pub struct RepeatingRadialGradientFunctionParams<'a>(
144 Option<RadialSize<'a>>,
145 Option<RadialShape>,
146 Option<T![Ident]>,
147 Option<Position<'a>>,
148 #[semantic_eq(skip)] Option<T![,]>,
149 CommaSeparated<'a, ColorStopOrHint<'a>>,
150);
151
152#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
153#[node]
154#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
156#[cfg_attr(feature = "visitable", visit(skip))]
157#[derive(csskit_derives::NodeWithMetadata)]
158pub enum NamedDirection {
159 #[atom(CssAtomSet::Bottom)]
160 Bottom(T![Ident]),
161 #[atom(CssAtomSet::Top)]
162 Top(T![Ident]),
163 #[atom(CssAtomSet::Left)]
164 Left(T![Ident]),
165 #[atom(CssAtomSet::Right)]
166 Right(T![Ident]),
167}
168
169#[node]
170#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
172#[derive(csskit_derives::NodeWithMetadata)]
173pub enum LinearDirection<'a> {
174 Angle(CalcableValue<'a, Angle>),
175 Named(#[atom(CssAtomSet::To)] T![Ident], NamedDirection, Option<NamedDirection>),
176}
177
178#[node]
186#[derive(Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
188#[derive(csskit_derives::NodeWithMetadata)]
189pub enum RadialSize<'a> {
190 Extent(RadialExtent, Option<RadialExtent>),
191 Circular(CalcableValue<'a, LengthPercentage>),
192 Elliptical(CalcableValue<'a, LengthPercentage>, CalcableValue<'a, LengthPercentage>),
193}
194
195#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
201#[node]
202#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
204#[cfg_attr(feature = "visitable", visit(skip))]
205#[derive(csskit_derives::NodeWithMetadata)]
206pub enum RadialExtent {
207 #[atom(CssAtomSet::ClosestCorner)]
208 ClosestCorner(T![Ident]),
209 #[atom(CssAtomSet::ClosestSide)]
210 ClosestSide(T![Ident]),
211 #[atom(CssAtomSet::FarthestCorner)]
212 FarthestCorner(T![Ident]),
213 #[atom(CssAtomSet::FarthestSide)]
214 FarthestSide(T![Ident]),
215}
216
217impl<'a> Parse<'a> for RadialSize<'a> {
218 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
219 where
220 I: Iterator<Item = Cursor> + Clone,
221 {
222 if let Some(extent) = p.parse_if_peek::<RadialExtent>()? {
223 let second = p.parse_if_peek::<RadialExtent>()?;
224 return Ok(RadialSize::Extent(extent, second));
225 }
226 let first = p.parse::<CalcableValue<LengthPercentage>>()?;
227 if let Some(second) = p.parse_if_peek::<CalcableValue<LengthPercentage>>()? {
228 return Ok(Self::Elliptical(first, second));
229 }
230 Ok(Self::Circular(first))
231 }
232}
233
234#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
240#[node]
241#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
242#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
243#[cfg_attr(feature = "visitable", visit(skip))]
244#[derive(csskit_derives::NodeWithMetadata)]
245pub enum RadialShape {
246 #[atom(CssAtomSet::Circle)]
247 Circle(T![Ident]),
248 #[atom(CssAtomSet::Ellipse)]
249 Ellipse(T![Ident]),
250}
251
252#[node]
253#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
254#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
255#[derive(csskit_derives::NodeWithMetadata)]
256pub enum ColorStopOrHint<'a> {
257 Hint(CalcableValue<'a, LengthPercentage>),
258 Stop(Color<'a>, Option<CalcableValue<'a, LengthPercentage>>),
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 use crate::CssAtomSet;
265 use css_parse::assert_parse;
266
267 #[test]
268 fn test_writes() {
269 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(to bottom,yellow,blue)");
270 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(yellow,blue)");
271 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(to bottom,#fff,#fff 85%,#e6e6e6)");
272 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(45deg,#808080 25%,transparent 25%)");
273 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(to right,transparent,red 20%,red 80%,transparent)");
274 assert_parse!(
275 CssAtomSet::ATOMS,
276 Gradient,
277 "radial-gradient(closest-corner circle,rgba(1,65,255,0.4),rgba(1,65,255,0))"
278 );
279 }
280
281 #[test]
282 fn test_substitution() {
283 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(calc(45deg),red,blue)");
285 assert_parse!(CssAtomSet::ATOMS, Gradient, "linear-gradient(red calc(20% + 5px),blue)");
286 assert_parse!(CssAtomSet::ATOMS, Gradient, "radial-gradient(calc(50px) circle,red,blue)");
287 }
288}