lightningcss/properties/
position.rs

1//! CSS properties related to positioning.
2
3use super::Property;
4use crate::context::PropertyHandlerContext;
5use crate::declaration::DeclarationList;
6use crate::error::{ParserError, PrinterError};
7use crate::prefixes::Feature;
8use crate::printer::Printer;
9use crate::traits::{Parse, PropertyHandler, ToCss};
10use crate::values::number::CSSInteger;
11use crate::vendor_prefix::VendorPrefix;
12#[cfg(feature = "visitor")]
13use crate::visitor::Visit;
14use cssparser::*;
15
16/// A value for the [position](https://www.w3.org/TR/css-position-3/#position-property) property.
17#[derive(Debug, Clone, PartialEq)]
18#[cfg_attr(feature = "visitor", derive(Visit))]
19#[cfg_attr(
20  feature = "serde",
21  derive(serde::Serialize, serde::Deserialize),
22  serde(tag = "type", content = "value", rename_all = "kebab-case")
23)]
24#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
25#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
26pub enum Position {
27  /// The box is laid in the document flow.
28  Static,
29  /// The box is laid out in the document flow and offset from the resulting position.
30  Relative,
31  /// The box is taken out of document flow and positioned in reference to its relative ancestor.
32  Absolute,
33  /// Similar to relative but adjusted according to the ancestor scrollable element.
34  Sticky(VendorPrefix),
35  /// The box is taken out of the document flow and positioned in reference to the page viewport.
36  Fixed,
37}
38
39impl<'i> Parse<'i> for Position {
40  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
41    let location = input.current_source_location();
42    let ident = input.expect_ident()?;
43    match_ignore_ascii_case! { &*ident,
44      "static" => Ok(Position::Static),
45      "relative" => Ok(Position::Relative),
46      "absolute" => Ok(Position::Absolute),
47      "fixed" => Ok(Position::Fixed),
48      "sticky" => Ok(Position::Sticky(VendorPrefix::None)),
49      "-webkit-sticky" => Ok(Position::Sticky(VendorPrefix::WebKit)),
50      _ => Err(location.new_unexpected_token_error(
51        cssparser::Token::Ident(ident.clone())
52      ))
53    }
54  }
55}
56
57impl ToCss for Position {
58  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
59  where
60    W: std::fmt::Write,
61  {
62    match self {
63      Position::Static => dest.write_str("static"),
64      Position::Relative => dest.write_str("relative"),
65      Position::Absolute => dest.write_str("absolute"),
66      Position::Fixed => dest.write_str("fixed"),
67      Position::Sticky(prefix) => {
68        prefix.to_css(dest)?;
69        dest.write_str("sticky")
70      }
71    }
72  }
73}
74
75/// A value for the [z-index](https://drafts.csswg.org/css2/#z-index) property.
76#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
77#[cfg_attr(feature = "visitor", derive(Visit))]
78#[cfg_attr(
79  feature = "serde",
80  derive(serde::Serialize, serde::Deserialize),
81  serde(tag = "type", content = "value", rename_all = "kebab-case")
82)]
83#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
84#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
85pub enum ZIndex {
86  /// The `auto` keyword.
87  Auto,
88  /// An integer value.
89  Integer(CSSInteger),
90}
91
92#[derive(Default)]
93pub(crate) struct PositionHandler {
94  position: Option<Position>,
95}
96
97impl<'i> PropertyHandler<'i> for PositionHandler {
98  fn handle_property(
99    &mut self,
100    property: &Property<'i>,
101    _: &mut DeclarationList<'i>,
102    _: &mut PropertyHandlerContext<'i, '_>,
103  ) -> bool {
104    if let Property::Position(position) = property {
105      if let (Some(Position::Sticky(cur)), Position::Sticky(new)) = (&mut self.position, position) {
106        *cur |= *new;
107      } else {
108        self.position = Some(position.clone());
109      }
110
111      return true;
112    }
113
114    false
115  }
116
117  fn finalize(&mut self, dest: &mut DeclarationList, context: &mut PropertyHandlerContext<'i, '_>) {
118    if self.position.is_none() {
119      return;
120    }
121
122    if let Some(position) = std::mem::take(&mut self.position) {
123      match position {
124        Position::Sticky(mut prefix) => {
125          prefix = context.targets.prefixes(prefix, Feature::Sticky);
126          if prefix.contains(VendorPrefix::WebKit) {
127            dest.push(Property::Position(Position::Sticky(VendorPrefix::WebKit)))
128          }
129
130          if prefix.contains(VendorPrefix::None) {
131            dest.push(Property::Position(Position::Sticky(VendorPrefix::None)))
132          }
133        }
134        _ => dest.push(Property::Position(position)),
135      }
136    }
137  }
138}