parcel_css/properties/
contain.rs

1//! CSS properties related to containment.
2
3#![allow(non_upper_case_globals)]
4
5use bitflags::bitflags;
6use cssparser::*;
7use smallvec::SmallVec;
8
9use crate::{
10  context::PropertyHandlerContext,
11  declaration::{DeclarationBlock, DeclarationList},
12  error::{ParserError, PrinterError},
13  macros::{define_shorthand, shorthand_handler},
14  printer::Printer,
15  properties::{Property, PropertyId},
16  rules::container::ContainerName as ContainerIdent,
17  targets::Browsers,
18  traits::{Parse, PropertyHandler, Shorthand, ToCss},
19};
20
21bitflags! {
22  /// A value for the [container-type](https://drafts.csswg.org/css-contain-3/#container-type) property.
23  /// Establishes the element as a query container for the purpose of container queries.
24  ///
25  /// `normal` is mutually exclusive, but other combinations of flags are allowed.
26  #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27  pub struct ContainerType: u8 {
28    /// The element is not a query container for any container size queries,
29    /// but remains a query container for container style queries.
30    const Normal    = 0b00000001;
31    /// Establishes a query container for container size queries on the container’s own inline axis.
32    const InlineSize = 0b00000010;
33    /// Establishes a query container for container size queries on both the inline and block axis.
34    const Size = 0b00000100;
35  }
36}
37
38impl Default for ContainerType {
39  fn default() -> Self {
40    ContainerType::Normal
41  }
42}
43
44impl<'i> Parse<'i> for ContainerType {
45  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
46    let mut flags = ContainerType::empty();
47    while let Ok(ident) = input.try_parse(|input| input.expect_ident_cloned()) {
48      let location = input.current_source_location();
49      let flag = match_ignore_ascii_case! { &ident,
50        "normal" if flags.is_empty() => return Ok(ContainerType::Normal), // mutually exclusive
51        "size" => ContainerType::Size,
52        "inline-size" => ContainerType::InlineSize,
53        _ => return Err(location.new_unexpected_token_error(
54          cssparser::Token::Ident(ident.clone())
55        ))
56      };
57      if flags.contains(flag) {
58        return Err(location.new_unexpected_token_error(cssparser::Token::Ident(ident.clone())));
59      }
60      flags |= flag;
61    }
62
63    if flags.is_empty() {
64      return Err(input.new_error_for_next_token());
65    } else {
66      return Ok(flags);
67    }
68  }
69}
70
71impl ToCss for ContainerType {
72  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
73  where
74    W: std::fmt::Write,
75  {
76    if self.contains(ContainerType::Normal) {
77      dest.write_str("normal")?;
78    } else if self.contains(ContainerType::Size) {
79      dest.write_str("size")?;
80    } else if self.contains(ContainerType::InlineSize) {
81      dest.write_str("inline-size")?;
82    }
83
84    Ok(())
85  }
86}
87
88/// A value for the [container-name](https://drafts.csswg.org/css-contain-3/#container-name) property.
89#[derive(Debug, Clone, PartialEq)]
90#[cfg_attr(
91  feature = "serde",
92  derive(serde::Serialize, serde::Deserialize),
93  serde(tag = "type", content = "value", rename_all = "kebab-case")
94)]
95pub enum ContainerNameList<'i> {
96  /// The `none` keyword.
97  None,
98  /// A list of container names.
99  #[cfg_attr(feature = "serde", serde(borrow))]
100  Names(SmallVec<[ContainerIdent<'i>; 1]>),
101}
102
103impl<'i> Default for ContainerNameList<'i> {
104  fn default() -> Self {
105    ContainerNameList::None
106  }
107}
108
109impl<'i> Parse<'i> for ContainerNameList<'i> {
110  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
111    if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
112      return Ok(ContainerNameList::None);
113    }
114
115    let mut names = SmallVec::new();
116    while let Ok(name) = input.try_parse(ContainerIdent::parse) {
117      names.push(name);
118    }
119
120    if names.is_empty() {
121      return Err(input.new_error_for_next_token());
122    } else {
123      return Ok(ContainerNameList::Names(names));
124    }
125  }
126}
127
128impl<'i> ToCss for ContainerNameList<'i> {
129  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
130  where
131    W: std::fmt::Write,
132  {
133    match self {
134      ContainerNameList::None => dest.write_str("none"),
135      ContainerNameList::Names(names) => {
136        let mut first = true;
137        for name in names {
138          if first {
139            first = false;
140          } else {
141            dest.write_char(' ')?;
142          }
143          name.to_css(dest)?;
144        }
145        Ok(())
146      }
147    }
148  }
149}
150
151define_shorthand! {
152  /// A value for the [container](https://drafts.csswg.org/css-contain-3/#container-shorthand) shorthand property.
153  pub struct Container<'i> {
154    /// The container name.
155    #[cfg_attr(feature = "serde", serde(borrow))]
156    name: ContainerName(ContainerNameList<'i>),
157    /// The container type.
158    container_type: ContainerType(ContainerType),
159  }
160}
161
162impl<'i> Parse<'i> for Container<'i> {
163  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
164    let name = ContainerNameList::parse(input)?;
165    let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
166      ContainerType::parse(input)?
167    } else {
168      ContainerType::default()
169    };
170    Ok(Container { name, container_type })
171  }
172}
173
174impl<'i> ToCss for Container<'i> {
175  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
176  where
177    W: std::fmt::Write,
178  {
179    self.name.to_css(dest)?;
180    if self.container_type != ContainerType::default() {
181      dest.delim('/', true)?;
182      self.container_type.to_css(dest)?;
183    }
184    Ok(())
185  }
186}
187
188shorthand_handler!(ContainerHandler -> Container<'i> {
189  name: ContainerName(ContainerNameList<'i>),
190  container_type: ContainerType(ContainerType),
191});