parcel_css/properties/
list.rs

1//! CSS properties related to lists and counters.
2
3use super::{Property, PropertyId};
4use crate::context::PropertyHandlerContext;
5use crate::declaration::{DeclarationBlock, DeclarationList};
6use crate::error::{ParserError, PrinterError};
7use crate::macros::{define_shorthand, enum_property, shorthand_handler, shorthand_property};
8use crate::printer::Printer;
9use crate::targets::Browsers;
10use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss};
11use crate::values::string::CowArcStr;
12use crate::values::{ident::CustomIdent, image::Image};
13use cssparser::*;
14
15/// A value for the [list-style-type](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#text-markers) property.
16#[derive(Debug, Clone, PartialEq)]
17#[cfg_attr(
18  feature = "serde",
19  derive(serde::Serialize, serde::Deserialize),
20  serde(tag = "type", content = "value", rename_all = "kebab-case")
21)]
22pub enum ListStyleType<'i> {
23  /// No marker.
24  None,
25  /// An explicit marker string.
26  #[cfg_attr(feature = "serde", serde(borrow))]
27  String(CowArcStr<'i>),
28  /// A named counter style.
29  CounterStyle(CounterStyle<'i>),
30}
31
32impl Default for ListStyleType<'_> {
33  fn default() -> Self {
34    ListStyleType::CounterStyle(CounterStyle::Predefined(PredefinedCounterStyle::Disc))
35  }
36}
37
38impl<'i> Parse<'i> for ListStyleType<'i> {
39  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
40    if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
41      return Ok(ListStyleType::None);
42    }
43
44    if let Ok(val) = input.try_parse(CounterStyle::parse) {
45      return Ok(ListStyleType::CounterStyle(val));
46    }
47
48    let s = input.expect_string_cloned()?;
49    Ok(ListStyleType::String(s.into()))
50  }
51}
52
53impl ToCss for ListStyleType<'_> {
54  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
55  where
56    W: std::fmt::Write,
57  {
58    match self {
59      ListStyleType::None => dest.write_str("none"),
60      ListStyleType::CounterStyle(style) => style.to_css(dest),
61      ListStyleType::String(s) => {
62        serialize_string(&s, dest)?;
63        Ok(())
64      }
65    }
66  }
67}
68
69/// A [counter-style](https://www.w3.org/TR/css-counter-styles-3/#typedef-counter-style) name.
70#[derive(Debug, Clone, PartialEq)]
71#[cfg_attr(
72  feature = "serde",
73  derive(serde::Serialize, serde::Deserialize),
74  serde(tag = "type", content = "value", rename_all = "kebab-case")
75)]
76pub enum CounterStyle<'i> {
77  /// A predefined counter style name.
78  Predefined(PredefinedCounterStyle),
79  /// A custom counter style name.
80  Name(CustomIdent<'i>),
81  /// An inline [`symbols()`](https://www.w3.org/TR/css-counter-styles-3/#symbols-function) definition.
82  Symbols(
83    SymbolsType,
84    #[cfg_attr(feature = "serde", serde(borrow))] Vec<Symbol<'i>>,
85  ),
86}
87
88enum_property! {
89  /// A [predefined counter](https://www.w3.org/TR/css-counter-styles-3/#predefined-counters) style.
90  #[allow(missing_docs)]
91  pub enum PredefinedCounterStyle {
92    // https://www.w3.org/TR/css-counter-styles-3/#simple-numeric
93    "decimal": Decimal,
94    "decimal-leading-zero": DecimalLeadingZero,
95    "arabic-indic": ArabicIndic,
96    "armenian": Armenian,
97    "upper-armenian": UpperArmenian,
98    "lower-armenian": LowerArmenian,
99    "bengali": Bengali,
100    "cambodian": Cambodian,
101    "khmer": Khmer,
102    "cjk-decimal": CjkDecimal,
103    "devanagari": Devanagari,
104    "georgian": Georgian,
105    "gujarati": Gujarati,
106    "gurmukhi": Gurmukhi,
107    "hebrew": Hebrew,
108    "kannada": Kannada,
109    "lao": Lao,
110    "malayalam": Malayalam,
111    "mongolian": Mongolian,
112    "myanmar": Myanmar,
113    "oriya": Oriya,
114    "persian": Persian,
115    "lower-roman": LowerRoman,
116    "upper-roman": UpperRoman,
117    "tamil": Tamil,
118    "telugu": Telugu,
119    "thai": Thai,
120    "tibetan": Tibetan,
121
122    // https://www.w3.org/TR/css-counter-styles-3/#simple-alphabetic
123    "lower-alpha": LowerAlpha,
124    "lower-latin": LowerLatin,
125    "upper-alpha": UpperAlpha,
126    "upper-latin": UpperLatin,
127    "lower-greek": LowerGreek,
128    "hiragana": Hiragana,
129    "hiragana-iroha": HiraganaIroha,
130    "katakana": Katakana,
131    "katakana-iroha": KatakanaIroha,
132
133    // https://www.w3.org/TR/css-counter-styles-3/#simple-symbolic
134    "disc": Disc,
135    "circle": Circle,
136    "square": Square,
137    "disclosure-open": DisclosureOpen,
138    "disclosure-closed": DisclosureClosed,
139
140    // https://www.w3.org/TR/css-counter-styles-3/#simple-fixed
141    "cjk-earthly-branch": CjkEarthlyBranch,
142    "cjk-heavenly-stem": CjkHeavenlyStem,
143
144    // https://www.w3.org/TR/css-counter-styles-3/#complex-cjk
145    "japanese-informal": JapaneseInformal,
146    "japanese-formal": JapaneseFormal,
147    "korean-hangul-formal": KoreanHangulFormal,
148    "korean-hanja-informal": KoreanHanjaInformal,
149    "korean-hanja-formal": KoreanHanjaFormal,
150    "simp-chinese-informal": SimpChineseInformal,
151    "simp-chinese-formal": SimpChineseFormal,
152    "trad-chinese-informal": TradChineseInformal,
153    "trad-chinese-formal": TradChineseFormal,
154    "ethiopic-numeric": EthiopicNumeric,
155  }
156}
157
158impl<'i> Parse<'i> for CounterStyle<'i> {
159  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
160    if let Ok(predefined) = input.try_parse(PredefinedCounterStyle::parse) {
161      return Ok(CounterStyle::Predefined(predefined));
162    }
163
164    if input.try_parse(|input| input.expect_function_matching("symbols")).is_ok() {
165      return input.parse_nested_block(|input| {
166        let t = input.try_parse(SymbolsType::parse).unwrap_or(SymbolsType::Symbolic);
167
168        let mut symbols = Vec::new();
169        while let Ok(s) = input.try_parse(Symbol::parse) {
170          symbols.push(s);
171        }
172
173        Ok(CounterStyle::Symbols(t, symbols))
174      });
175    }
176
177    let name = CustomIdent::parse(input)?;
178    Ok(CounterStyle::Name(name))
179  }
180}
181
182impl ToCss for CounterStyle<'_> {
183  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
184  where
185    W: std::fmt::Write,
186  {
187    match self {
188      CounterStyle::Predefined(style) => style.to_css(dest),
189      CounterStyle::Name(name) => {
190        if let Some(css_module) = &mut dest.css_module {
191          css_module.reference(&name.0)
192        }
193        name.to_css(dest)
194      }
195      CounterStyle::Symbols(t, symbols) => {
196        dest.write_str("symbols(")?;
197        let mut needs_space = false;
198        if *t != SymbolsType::Symbolic {
199          t.to_css(dest)?;
200          needs_space = true;
201        }
202
203        for symbol in symbols {
204          if needs_space {
205            dest.write_char(' ')?;
206          }
207          symbol.to_css(dest)?;
208          needs_space = true;
209        }
210        dest.write_char(')')
211      }
212    }
213  }
214}
215
216enum_property! {
217  /// A [`<symbols-type>`](https://www.w3.org/TR/css-counter-styles-3/#typedef-symbols-type) value,
218  /// as used in the `symbols()` function.
219  ///
220  /// See [CounterStyle](CounterStyle).
221  #[allow(missing_docs)]
222  pub enum SymbolsType {
223    Cyclic,
224    Numeric,
225    Alphabetic,
226    Symbolic,
227    Fixed,
228  }
229}
230
231/// A single [symbol](https://www.w3.org/TR/css-counter-styles-3/#funcdef-symbols) as used in the
232/// `symbols()` function.
233///
234/// See [CounterStyle](CounterStyle).
235#[derive(Debug, Clone, PartialEq)]
236#[cfg_attr(
237  feature = "serde",
238  derive(serde::Serialize, serde::Deserialize),
239  serde(tag = "type", content = "value", rename_all = "kebab-case")
240)]
241pub enum Symbol<'i> {
242  /// A string.
243  #[cfg_attr(feature = "serde", serde(borrow))]
244  String(CowArcStr<'i>),
245  /// An image.
246  Image(Image<'i>),
247}
248
249impl<'i> Parse<'i> for Symbol<'i> {
250  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
251    if let Ok(img) = input.try_parse(Image::parse) {
252      return Ok(Symbol::Image(img));
253    }
254
255    let s = input.expect_string_cloned()?;
256    Ok(Symbol::String(s.into()))
257  }
258}
259
260impl<'i> ToCss for Symbol<'i> {
261  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
262  where
263    W: std::fmt::Write,
264  {
265    match self {
266      Symbol::String(s) => {
267        serialize_string(&s, dest)?;
268        Ok(())
269      }
270      Symbol::Image(img) => img.to_css(dest),
271    }
272  }
273}
274
275enum_property! {
276  /// A value for the [list-style-position](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#list-style-position-property) property.
277  pub enum ListStylePosition {
278    /// The list marker is placed inside the element.
279    Inside,
280    /// The list marker is placed outside the element.
281    Outside,
282  }
283}
284
285impl Default for ListStylePosition {
286  fn default() -> ListStylePosition {
287    ListStylePosition::Outside
288  }
289}
290
291enum_property! {
292  /// A value for the [marker-side](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#marker-side) property.
293  #[allow(missing_docs)]
294  pub enum MarkerSide {
295    "match-self": MatchSelf,
296    "match-parent": MatchParent,
297  }
298}
299
300shorthand_property! {
301  /// A value for the [list-style](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#list-style-property) shorthand property.
302  pub struct ListStyle<'i> {
303    /// The list style type.
304    #[cfg_attr(feature = "serde", serde(borrow))]
305    list_style_type: ListStyleType(ListStyleType<'i>),
306    /// The list marker image.
307    image: ListStyleImage(Image<'i>),
308    /// The position of the list marker.
309    position: ListStylePosition(ListStylePosition),
310  }
311}
312
313impl<'i> FallbackValues for ListStyle<'i> {
314  fn get_fallbacks(&mut self, targets: Browsers) -> Vec<Self> {
315    self
316      .image
317      .get_fallbacks(targets)
318      .into_iter()
319      .map(|image| ListStyle { image, ..self.clone() })
320      .collect()
321  }
322}
323
324shorthand_handler!(ListStyleHandler -> ListStyle<'i> fallbacks: true {
325  image: ListStyleImage(Image<'i>, fallback: true, image: true),
326  list_style_type: ListStyleType(ListStyleType<'i>),
327  position: ListStylePosition(ListStylePosition),
328});