mod animation;
mod aspect_ratio;
mod background;
mod background_image;
mod background_position;
mod background_repeat;
mod background_size;
mod background_size_resolve;
mod blend_mode;
mod border;
mod box_alignment;
mod box_shadow;
mod clip_path;
mod color;
mod conic_gradient;
mod content;
mod filter;
mod flex;
mod flex_grow;
mod font_family;
mod font_feature_settings;
mod font_size;
mod font_stretch;
mod font_style;
mod font_synthesis;
mod font_variation_settings;
mod font_weight;
mod gradient_utils;
mod grid;
mod length;
mod line_clamp;
mod line_height;
mod linear_gradient;
mod order;
mod overflow;
mod overflow_wrap;
mod percentage_number;
mod radial_gradient;
mod sides;
mod space_pair;
mod text_decoration;
mod text_fit;
mod text_indent;
mod text_overflow;
mod text_shadow;
mod text_stroke;
mod text_wrap;
mod traits;
mod transform;
mod vertical_align;
mod white_space;
mod word_break;
mod z_index;
pub use animation::*;
pub use aspect_ratio::*;
pub use background::*;
pub use background_image::*;
pub use background_position::*;
pub use background_repeat::*;
pub use background_size::*;
pub use background_size_resolve::*;
pub use blend_mode::*;
pub use border::*;
pub use box_alignment::*;
pub use box_shadow::*;
pub use clip_path::*;
pub use color::*;
pub use conic_gradient::*;
pub use content::*;
pub use filter::*;
pub use flex::*;
pub use flex_grow::*;
pub use font_family::*;
pub use font_feature_settings::*;
pub use font_size::*;
pub use font_stretch::*;
pub use font_style::*;
pub use font_synthesis::*;
pub use font_variation_settings::*;
pub use font_weight::*;
pub use gradient_utils::{GradientOverlayTile, overlay_gradient_tile_fast_normal_unconstrained};
pub use grid::*;
pub use length::*;
pub use line_clamp::*;
pub use line_height::*;
pub use linear_gradient::*;
pub use order::*;
pub use overflow::*;
pub use overflow_wrap::*;
pub use percentage_number::*;
pub use radial_gradient::*;
use serde::Deserialize;
pub use sides::*;
pub use space_pair::*;
pub use text_decoration::*;
pub use text_fit::*;
pub use text_indent::*;
pub use text_overflow::*;
pub use text_shadow::*;
pub use text_stroke::*;
pub use text_wrap::*;
pub use traits::*;
pub(crate) use traits::{
declare_box_alignment_enum_impl, declare_enum_from_css_impl, impl_from_taffy_enum,
};
pub use transform::*;
pub use vertical_align::*;
pub use white_space::*;
pub use word_break::*;
pub use z_index::*;
use cssparser::{Parser, match_ignore_ascii_case};
use image::imageops::FilterType;
use parley::Alignment;
use std::fmt;
use crate::style::{SizingContext, tw::TailwindPropertyParser};
pub(crate) fn next_is_comma<'i>(input: &mut Parser<'i, '_>) -> bool {
let state = input.state();
let is_comma = input.expect_comma().is_ok();
input.reset(&state);
is_comma
}
impl<T: Animatable + Copy> Animatable for SpacePair<T> {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &SizingContext,
current_color: Color,
) {
self
.x
.interpolate(&from.x, &to.x, progress, sizing, current_color);
self
.y
.interpolate(&from.y, &to.y, progress, sizing, current_color);
}
}
impl<T: Animatable + Copy> Animatable for Sides<T> {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &SizingContext,
current_color: Color,
) {
for (index, value) in self.0.iter_mut().enumerate() {
value.interpolate(
&from.0[index],
&to.0[index],
progress,
sizing,
current_color,
);
}
}
}
macro_rules! unexpected_token {
(@build $type:ty, $location:expr, $token:expr $(,)?) => {{
let location = $location;
let token = $token;
let token = cssparser::ToCss::to_css_string(token);
let message = <$type as $crate::style::FromCss>::EXPECT_MESSAGE
.build_message(&token, $crate::style::merge_enum_values(<$type as $crate::style::FromCss>::VALID_TOKENS));
cssparser::ParseError {
location,
kind: cssparser::ParseErrorKind::Custom(std::borrow::Cow::Owned(message)),
}
}};
($type:ty, $location:expr, $token:expr $(,)?) => {
$crate::style::unexpected_token!(@build $type, $location, $token)
};
($location:expr, $token:expr $(,)?) => {
$crate::style::unexpected_token!(@build Self, $location, $token)
};
}
pub(crate) use unexpected_token;
pub(crate) fn merge_enum_values(values: &[CssToken]) -> String {
match values.len() {
0 => String::new(),
1 => values[0].to_string(),
2 => format!("{} or {}", values[0], values[1]),
_ => {
let all_but_last = values[..values.len() - 1]
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ");
format!("{} or {}", all_but_last, values[values.len() - 1])
}
}
}
pub(crate) fn write_css_string<W: fmt::Write>(dest: &mut W, s: &str) -> fmt::Result {
dest.write_char('"')?;
for ch in s.chars() {
match ch {
'\\' => dest.write_str("\\\\")?,
'"' => dest.write_str("\\\"")?,
c => dest.write_char(c)?,
}
}
dest.write_char('"')
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ObjectFit {
#[default]
Fill,
Contain,
Cover,
ScaleDown,
None,
}
declare_enum_from_css_impl!(
ObjectFit,
"fill" => ObjectFit::Fill,
"contain" => ObjectFit::Contain,
"cover" => ObjectFit::Cover,
"scale-down" => ObjectFit::ScaleDown,
"none" => ObjectFit::None
);
impl TailwindPropertyParser for ObjectFit {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum BackgroundClip {
#[default]
BorderBox,
PaddingBox,
ContentBox,
Text,
BorderArea,
}
declare_enum_from_css_impl!(
BackgroundClip,
"border-box" => BackgroundClip::BorderBox,
"padding-box" => BackgroundClip::PaddingBox,
"content-box" => BackgroundClip::ContentBox,
"text" => BackgroundClip::Text,
"border-area" => BackgroundClip::BorderArea
);
impl TailwindPropertyParser for BackgroundClip {
fn parse_tw(token: &str) -> Option<Self> {
match_ignore_ascii_case! {token,
"border" => Some(BackgroundClip::BorderBox),
"padding" => Some(BackgroundClip::PaddingBox),
"content" => Some(BackgroundClip::ContentBox),
"text" => Some(BackgroundClip::Text),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct BorderRadius(pub Sides<SpacePair<LengthDefaultsToZero>>);
impl From<f32> for BorderRadius {
fn from(value: f32) -> Self {
Self(Sides(
[SpacePair::from_pair(Length::Px(value), Length::Px(value)); 4],
))
}
}
impl MakeComputed for BorderRadius {
fn make_computed(&mut self, sizing: &SizingContext) {
self.0.make_computed(sizing);
}
}
impl Animatable for BorderRadius {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &SizingContext,
current_color: Color,
) {
self
.0
.interpolate(&from.0, &to.0, progress, sizing, current_color);
}
}
impl<'i> FromCss<'i> for BorderRadius {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
let widths: Sides<LengthDefaultsToZero> = Sides::from_css(input)?;
let heights = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
Sides::from_css(input)?
} else {
widths
};
Ok(BorderRadius(Sides([
SpacePair::from_pair(widths.0[0], heights.0[0]),
SpacePair::from_pair(widths.0[1], heights.0[1]),
SpacePair::from_pair(widths.0[2], heights.0[2]),
SpacePair::from_pair(widths.0[3], heights.0[3]),
])))
}
const EXPECT_MESSAGE: CssExpectedMessage = CssExpectedMessage::BorderRadius;
const VALID_TOKENS: &'static [CssToken] = &[CssToken::Syntax(CssSyntaxKind::Length)];
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum BoxSizing {
ContentBox,
#[default]
BorderBox,
}
declare_enum_from_css_impl!(
BoxSizing,
"content-box" => BoxSizing::ContentBox,
"border-box" => BoxSizing::BorderBox
);
impl_from_taffy_enum!(BoxSizing, taffy::BoxSizing, ContentBox, BorderBox);
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum TextAlign {
Left,
Right,
Center,
Justify,
#[default]
Start,
End,
}
declare_enum_from_css_impl!(
TextAlign,
"left" => TextAlign::Left,
"right" => TextAlign::Right,
"center" => TextAlign::Center,
"justify" => TextAlign::Justify,
"start" => TextAlign::Start,
"end" => TextAlign::End
);
impl TailwindPropertyParser for TextAlign {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
impl_from_taffy_enum!(
TextAlign, Alignment, Left, Right, Center, Justify, Start, End
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum Isolation {
Isolate,
#[default]
Auto,
}
declare_enum_from_css_impl!(
Isolation,
"isolate" => Isolation::Isolate,
"auto" => Isolation::Auto
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum Visibility {
#[default]
Visible,
Hidden,
}
declare_enum_from_css_impl!(
Visibility,
"visible" => Visibility::Visible,
"hidden" => Visibility::Hidden
);
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum LineJoin {
#[default]
Miter,
Round,
Bevel,
}
declare_enum_from_css_impl!(
LineJoin,
"miter" => LineJoin::Miter,
"round" => LineJoin::Round,
"bevel" => LineJoin::Bevel
);
impl TailwindPropertyParser for LineJoin {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum Position {
#[default]
Relative,
Absolute,
Static,
Fixed,
}
declare_enum_from_css_impl!(
Position,
"relative" => Position::Relative,
"absolute" => Position::Absolute,
"static" => Position::Static,
"fixed" => Position::Fixed
);
impl From<Position> for taffy::Position {
fn from(value: Position) -> Self {
match value {
Position::Relative | Position::Static => Self::Relative,
Position::Absolute | Position::Fixed => Self::Absolute,
}
}
}
impl Position {
pub const fn is_positioned(self) -> bool {
matches!(self, Self::Relative | Self::Absolute | Self::Fixed)
}
pub const fn is_out_of_flow(self) -> bool {
matches!(self, Self::Absolute | Self::Fixed)
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Direction {
#[default]
Ltr,
Rtl,
}
declare_enum_from_css_impl!(
Direction,
"ltr" => Direction::Ltr,
"rtl" => Direction::Rtl
);
impl_from_taffy_enum!(Direction, taffy::Direction, Ltr, Rtl);
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum Float {
#[default]
None,
Left,
Right,
InlineStart,
InlineEnd,
}
declare_enum_from_css_impl!(
Float,
"none" => Float::None,
"left" => Float::Left,
"right" => Float::Right,
"inline-start" => Float::InlineStart,
"inline-end" => Float::InlineEnd,
);
impl Float {
pub fn resolve(self, direction: Direction) -> taffy::Float {
match self {
Self::None => taffy::Float::None,
Self::Left => taffy::Float::Left,
Self::Right => taffy::Float::Right,
Self::InlineStart => {
if direction == Direction::Rtl {
taffy::Float::Right
} else {
taffy::Float::Left
}
}
Self::InlineEnd => {
if direction == Direction::Rtl {
taffy::Float::Left
} else {
taffy::Float::Right
}
}
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum Clear {
#[default]
None,
Left,
Right,
Both,
InlineStart,
InlineEnd,
}
declare_enum_from_css_impl!(
Clear,
"none" => Clear::None,
"left" => Clear::Left,
"right" => Clear::Right,
"both" => Clear::Both,
"inline-start" => Clear::InlineStart,
"inline-end" => Clear::InlineEnd,
);
impl Clear {
pub fn resolve(self, direction: Direction) -> taffy::Clear {
match self {
Self::None => taffy::Clear::None,
Self::Left => taffy::Clear::Left,
Self::Right => taffy::Clear::Right,
Self::Both => taffy::Clear::Both,
Self::InlineStart => {
if direction == Direction::Rtl {
taffy::Clear::Right
} else {
taffy::Clear::Left
}
}
Self::InlineEnd => {
if direction == Direction::Rtl {
taffy::Clear::Left
} else {
taffy::Clear::Right
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum FlexDirection {
#[default]
Row,
Column,
RowReverse,
ColumnReverse,
}
declare_enum_from_css_impl!(
FlexDirection,
"row" => FlexDirection::Row,
"column" => FlexDirection::Column,
"row-reverse" => FlexDirection::RowReverse,
"column-reverse" => FlexDirection::ColumnReverse
);
impl_from_taffy_enum!(
FlexDirection,
taffy::FlexDirection,
Row,
Column,
RowReverse,
ColumnReverse
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum JustifyContent {
#[default]
Normal,
Start,
End,
FlexStart,
FlexEnd,
Center,
Stretch,
SpaceBetween,
SpaceEvenly,
SpaceAround,
SafeStart,
SafeEnd,
SafeFlexStart,
SafeFlexEnd,
SafeCenter,
}
declare_box_alignment_enum_impl!(
JustifyContent,
safe {
"start" => Start / SafeStart,
"end" => End / SafeEnd,
"flex-start" => FlexStart / SafeFlexStart,
"flex-end" => FlexEnd / SafeFlexEnd,
"center" => Center / SafeCenter,
},
plain {
"normal" => Normal,
"stretch" => Stretch,
"space-between" => SpaceBetween,
"space-around" => SpaceAround,
"space-evenly" => SpaceEvenly,
}
);
impl TailwindPropertyParser for JustifyContent {
fn parse_tw(token: &str) -> Option<Self> {
match token {
"between" => Some(JustifyContent::SpaceBetween),
"around" => Some(JustifyContent::SpaceAround),
"evenly" => Some(JustifyContent::SpaceEvenly),
_ => Self::from_str(token).ok(),
}
}
}
impl From<JustifyContent> for Option<taffy::JustifyContent> {
fn from(value: JustifyContent) -> Self {
match value {
JustifyContent::Normal => None,
JustifyContent::Start => Some(taffy::JustifyContent::START),
JustifyContent::End => Some(taffy::JustifyContent::END),
JustifyContent::FlexStart => Some(taffy::JustifyContent::FLEX_START),
JustifyContent::FlexEnd => Some(taffy::JustifyContent::FLEX_END),
JustifyContent::Center => Some(taffy::JustifyContent::CENTER),
JustifyContent::Stretch => Some(taffy::JustifyContent::STRETCH),
JustifyContent::SpaceBetween => Some(taffy::JustifyContent::SPACE_BETWEEN),
JustifyContent::SpaceAround => Some(taffy::JustifyContent::SPACE_AROUND),
JustifyContent::SpaceEvenly => Some(taffy::JustifyContent::SPACE_EVENLY),
JustifyContent::SafeStart => Some(taffy::JustifyContent::SAFE_START),
JustifyContent::SafeEnd => Some(taffy::JustifyContent::SAFE_END),
JustifyContent::SafeFlexStart => Some(taffy::JustifyContent::SAFE_FLEX_START),
JustifyContent::SafeFlexEnd => Some(taffy::JustifyContent::SAFE_FLEX_END),
JustifyContent::SafeCenter => Some(taffy::JustifyContent::SAFE_CENTER),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum Display {
None,
Flex,
InlineFlex,
Grid,
InlineGrid,
#[default]
Inline,
Block,
InlineBlock,
}
declare_enum_from_css_impl!(
Display,
"none" => Display::None,
"flex" => Display::Flex,
"inline-flex" => Display::InlineFlex,
"grid" => Display::Grid,
"inline-grid" => Display::InlineGrid,
"inline" => Display::Inline,
"block" => Display::Block,
"inline-block" => Display::InlineBlock
);
impl Display {
pub fn is_inline(&self) -> bool {
*self == Display::Inline
}
pub fn is_inline_level(&self) -> bool {
matches!(
self,
Display::Inline | Display::InlineBlock | Display::InlineFlex | Display::InlineGrid
)
}
pub fn should_blockify_children(&self) -> bool {
matches!(
self,
Display::Flex | Display::InlineFlex | Display::Grid | Display::InlineGrid
)
}
pub fn as_blockified(self) -> Self {
match self {
Display::Inline => Display::Block,
Display::InlineBlock => Display::Block,
Display::InlineFlex => Display::Flex,
Display::InlineGrid => Display::Grid,
_ => self,
}
}
pub fn blockify(&mut self) {
*self = self.as_blockified();
}
}
impl From<Display> for taffy::Display {
fn from(value: Display) -> Self {
match value {
Display::Flex | Display::InlineFlex => taffy::Display::Flex,
Display::Grid | Display::InlineGrid => taffy::Display::Grid,
Display::Block | Display::InlineBlock | Display::Inline => taffy::Display::Block,
Display::None => taffy::Display::None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum AlignItems {
#[default]
Normal,
Start,
End,
FlexStart,
FlexEnd,
Center,
Baseline,
Stretch,
SafeStart,
SafeEnd,
SafeFlexStart,
SafeFlexEnd,
SafeCenter,
}
declare_box_alignment_enum_impl!(
AlignItems,
safe {
"start" => Start / SafeStart,
"end" => End / SafeEnd,
"flex-start" => FlexStart / SafeFlexStart,
"flex-end" => FlexEnd / SafeFlexEnd,
"center" => Center / SafeCenter,
},
plain {
"normal" => Normal,
"baseline" => Baseline,
"stretch" => Stretch,
}
);
impl TailwindPropertyParser for AlignItems {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
impl From<AlignItems> for Option<taffy::AlignItems> {
fn from(value: AlignItems) -> Self {
match value {
AlignItems::Normal => None,
AlignItems::Start => Some(taffy::AlignItems::START),
AlignItems::End => Some(taffy::AlignItems::END),
AlignItems::FlexStart => Some(taffy::AlignItems::FLEX_START),
AlignItems::FlexEnd => Some(taffy::AlignItems::FLEX_END),
AlignItems::Center => Some(taffy::AlignItems::CENTER),
AlignItems::Baseline => Some(taffy::AlignItems::BASELINE),
AlignItems::Stretch => Some(taffy::AlignItems::STRETCH),
AlignItems::SafeStart => Some(taffy::AlignItems::SAFE_START),
AlignItems::SafeEnd => Some(taffy::AlignItems::SAFE_END),
AlignItems::SafeFlexStart => Some(taffy::AlignItems::SAFE_FLEX_START),
AlignItems::SafeFlexEnd => Some(taffy::AlignItems::SAFE_FLEX_END),
AlignItems::SafeCenter => Some(taffy::AlignItems::SAFE_CENTER),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum FlexWrap {
#[default]
NoWrap,
Wrap,
WrapReverse,
}
declare_enum_from_css_impl!(
FlexWrap,
"nowrap" => FlexWrap::NoWrap,
"wrap" => FlexWrap::Wrap,
"wrap-reverse" => FlexWrap::WrapReverse
);
impl_from_taffy_enum!(FlexWrap, taffy::FlexWrap, NoWrap, Wrap, WrapReverse);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum TextTransform {
#[default]
None,
Uppercase,
Lowercase,
Capitalize,
}
declare_enum_from_css_impl!(
TextTransform,
"none" => TextTransform::None,
"uppercase" => TextTransform::Uppercase,
"lowercase" => TextTransform::Lowercase,
"capitalize" => TextTransform::Capitalize
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum TextDecorationSkipInk {
#[default]
Auto,
None,
}
declare_enum_from_css_impl!(
TextDecorationSkipInk,
"auto" => TextDecorationSkipInk::Auto,
"none" => TextDecorationSkipInk::None
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum WhiteSpaceCollapse {
Preserve,
#[default]
Collapse,
PreserveSpaces,
PreserveBreaks,
}
declare_enum_from_css_impl!(
WhiteSpaceCollapse,
"preserve" => WhiteSpaceCollapse::Preserve,
"collapse" => WhiteSpaceCollapse::Collapse,
"preserve-spaces" => WhiteSpaceCollapse::PreserveSpaces,
"preserve-breaks" => WhiteSpaceCollapse::PreserveBreaks,
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ImageScalingAlgorithm {
#[default]
Auto,
Smooth,
Pixelated,
}
declare_enum_from_css_impl!(
ImageScalingAlgorithm,
"auto" => ImageScalingAlgorithm::Auto,
"smooth" => ImageScalingAlgorithm::Smooth,
"pixelated" => ImageScalingAlgorithm::Pixelated
);
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum BorderStyle {
#[default]
None,
Hidden,
Dotted,
Dashed,
Solid,
Double,
Groove,
Ridge,
Inset,
Outset,
}
impl BorderStyle {
pub const fn is_rendered(self) -> bool {
!matches!(self, Self::None | Self::Hidden)
}
}
declare_enum_from_css_impl!(
BorderStyle,
"none" => BorderStyle::None,
"hidden" => BorderStyle::Hidden,
"dotted" => BorderStyle::Dotted,
"dashed" => BorderStyle::Dashed,
"solid" => BorderStyle::Solid,
"double" => BorderStyle::Double,
"groove" => BorderStyle::Groove,
"ridge" => BorderStyle::Ridge,
"inset" => BorderStyle::Inset,
"outset" => BorderStyle::Outset,
);
impl TailwindPropertyParser for BorderStyle {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
impl From<ImageScalingAlgorithm> for FilterType {
fn from(algorithm: ImageScalingAlgorithm) -> Self {
match algorithm {
ImageScalingAlgorithm::Auto => FilterType::CatmullRom,
ImageScalingAlgorithm::Smooth => FilterType::Lanczos3,
ImageScalingAlgorithm::Pixelated => FilterType::Nearest,
}
}
}