use super::{Property, PropertyId};
use crate::context::PropertyHandlerContext;
use crate::declaration::{DeclarationBlock, DeclarationList};
use crate::error::{ParserError, PrinterError};
use crate::macros::{define_shorthand, enum_property, shorthand_handler, shorthand_property};
use crate::printer::Printer;
use crate::targets::Browsers;
use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss};
use crate::values::string::CSSString;
use crate::values::{ident::CustomIdent, image::Image};
#[cfg(feature = "visitor")]
use crate::visitor::Visit;
use cssparser::*;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(lightningcss_derive::IntoOwned))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type", content = "value", rename_all = "kebab-case")
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum ListStyleType<'i> {
None,
#[cfg_attr(feature = "serde", serde(borrow))]
String(CSSString<'i>),
CounterStyle(CounterStyle<'i>),
}
impl Default for ListStyleType<'_> {
fn default() -> Self {
ListStyleType::CounterStyle(CounterStyle::Predefined(PredefinedCounterStyle::Disc))
}
}
impl<'i> Parse<'i> for ListStyleType<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(ListStyleType::None);
}
if let Ok(val) = input.try_parse(CounterStyle::parse) {
return Ok(ListStyleType::CounterStyle(val));
}
let s = CSSString::parse(input)?;
Ok(ListStyleType::String(s))
}
}
impl ToCss for ListStyleType<'_> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
ListStyleType::None => dest.write_str("none"),
ListStyleType::CounterStyle(style) => style.to_css(dest),
ListStyleType::String(s) => s.to_css(dest),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(lightningcss_derive::IntoOwned))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type", rename_all = "kebab-case")
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum CounterStyle<'i> {
#[cfg_attr(
feature = "serde",
serde(with = "crate::serialization::ValueWrapper::<PredefinedCounterStyle>")
)]
Predefined(PredefinedCounterStyle),
#[cfg_attr(
feature = "serde",
serde(borrow, with = "crate::serialization::ValueWrapper::<CustomIdent>")
)]
Name(CustomIdent<'i>),
Symbols {
#[cfg_attr(feature = "serde", serde(default))]
system: SymbolsType,
symbols: Vec<Symbol<'i>>,
},
}
enum_property! {
#[allow(missing_docs)]
pub enum PredefinedCounterStyle {
"decimal": Decimal,
"decimal-leading-zero": DecimalLeadingZero,
"arabic-indic": ArabicIndic,
"armenian": Armenian,
"upper-armenian": UpperArmenian,
"lower-armenian": LowerArmenian,
"bengali": Bengali,
"cambodian": Cambodian,
"khmer": Khmer,
"cjk-decimal": CjkDecimal,
"devanagari": Devanagari,
"georgian": Georgian,
"gujarati": Gujarati,
"gurmukhi": Gurmukhi,
"hebrew": Hebrew,
"kannada": Kannada,
"lao": Lao,
"malayalam": Malayalam,
"mongolian": Mongolian,
"myanmar": Myanmar,
"oriya": Oriya,
"persian": Persian,
"lower-roman": LowerRoman,
"upper-roman": UpperRoman,
"tamil": Tamil,
"telugu": Telugu,
"thai": Thai,
"tibetan": Tibetan,
"lower-alpha": LowerAlpha,
"lower-latin": LowerLatin,
"upper-alpha": UpperAlpha,
"upper-latin": UpperLatin,
"lower-greek": LowerGreek,
"hiragana": Hiragana,
"hiragana-iroha": HiraganaIroha,
"katakana": Katakana,
"katakana-iroha": KatakanaIroha,
"disc": Disc,
"circle": Circle,
"square": Square,
"disclosure-open": DisclosureOpen,
"disclosure-closed": DisclosureClosed,
"cjk-earthly-branch": CjkEarthlyBranch,
"cjk-heavenly-stem": CjkHeavenlyStem,
"japanese-informal": JapaneseInformal,
"japanese-formal": JapaneseFormal,
"korean-hangul-formal": KoreanHangulFormal,
"korean-hanja-informal": KoreanHanjaInformal,
"korean-hanja-formal": KoreanHanjaFormal,
"simp-chinese-informal": SimpChineseInformal,
"simp-chinese-formal": SimpChineseFormal,
"trad-chinese-informal": TradChineseInformal,
"trad-chinese-formal": TradChineseFormal,
"ethiopic-numeric": EthiopicNumeric,
}
}
impl<'i> Parse<'i> for CounterStyle<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if let Ok(predefined) = input.try_parse(PredefinedCounterStyle::parse) {
return Ok(CounterStyle::Predefined(predefined));
}
if input.try_parse(|input| input.expect_function_matching("symbols")).is_ok() {
return input.parse_nested_block(|input| {
let t = input.try_parse(SymbolsType::parse).unwrap_or_default();
let mut symbols = Vec::new();
while let Ok(s) = input.try_parse(Symbol::parse) {
symbols.push(s);
}
Ok(CounterStyle::Symbols { system: t, symbols })
});
}
let name = CustomIdent::parse(input)?;
Ok(CounterStyle::Name(name))
}
}
impl ToCss for CounterStyle<'_> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
CounterStyle::Predefined(style) => style.to_css(dest),
CounterStyle::Name(name) => {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&name.0, dest.loc.source_index)
}
name.to_css(dest)
}
CounterStyle::Symbols { system: t, symbols } => {
dest.write_str("symbols(")?;
let mut needs_space = false;
if *t != SymbolsType::Symbolic {
t.to_css(dest)?;
needs_space = true;
}
for symbol in symbols {
if needs_space {
dest.write_char(' ')?;
}
symbol.to_css(dest)?;
needs_space = true;
}
dest.write_char(')')
}
}
}
}
enum_property! {
#[allow(missing_docs)]
pub enum SymbolsType {
Cyclic,
Numeric,
Alphabetic,
Symbolic,
Fixed,
}
}
impl Default for SymbolsType {
fn default() -> Self {
SymbolsType::Symbolic
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(lightningcss_derive::IntoOwned))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type", content = "value", rename_all = "kebab-case")
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum Symbol<'i> {
#[cfg_attr(feature = "serde", serde(borrow))]
String(CSSString<'i>),
Image(Image<'i>),
}
impl<'i> Parse<'i> for Symbol<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if let Ok(img) = input.try_parse(Image::parse) {
return Ok(Symbol::Image(img));
}
let s = CSSString::parse(input)?;
Ok(Symbol::String(s.into()))
}
}
impl<'i> ToCss for Symbol<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Symbol::String(s) => s.to_css(dest),
Symbol::Image(img) => img.to_css(dest),
}
}
}
enum_property! {
pub enum ListStylePosition {
Inside,
Outside,
}
}
impl Default for ListStylePosition {
fn default() -> ListStylePosition {
ListStylePosition::Outside
}
}
enum_property! {
#[allow(missing_docs)]
pub enum MarkerSide {
"match-self": MatchSelf,
"match-parent": MatchParent,
}
}
shorthand_property! {
#[cfg_attr(feature = "into_owned", derive(lightningcss_derive::IntoOwned))]
pub struct ListStyle<'i> {
#[cfg_attr(feature = "serde", serde(borrow))]
list_style_type: ListStyleType(ListStyleType<'i>),
image: ListStyleImage(Image<'i>),
position: ListStylePosition(ListStylePosition),
}
}
impl<'i> FallbackValues for ListStyle<'i> {
fn get_fallbacks(&mut self, targets: Browsers) -> Vec<Self> {
self
.image
.get_fallbacks(targets)
.into_iter()
.map(|image| ListStyle { image, ..self.clone() })
.collect()
}
}
shorthand_handler!(ListStyleHandler -> ListStyle<'i> fallbacks: true {
image: ListStyleImage(Image<'i>, fallback: true, image: true),
list_style_type: ListStyleType(ListStyleType<'i>),
position: ListStylePosition(ListStylePosition),
});