Skip to main content

lightningcss/properties/
contain.rs

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