media queries

This commit is contained in:
annieversary 2022-07-10 19:31:09 +01:00
parent 78a7799933
commit 0f09a5e22d
4 changed files with 129 additions and 3 deletions

View File

@ -1,4 +1,8 @@
use crate::{Zephyr, ZephyrError}; use crate::{
media_queries::{ReducedMotion, Responsive},
modifiers::Modifiers,
Zephyr, ZephyrError,
};
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub(crate) struct Class<'a> { pub(crate) struct Class<'a> {
@ -6,7 +10,7 @@ pub(crate) struct Class<'a> {
pub value: Option<&'a str>, pub value: Option<&'a str>,
/// if true, no replacements will be done on `value` /// if true, no replacements will be done on `value`
pub value_literal: bool, pub value_literal: bool,
pub modifiers: Vec<&'a str>, pub modifiers: Modifiers<'a>,
pub pseudo: Option<&'a str>, pub pseudo: Option<&'a str>,
/// the original unparsed value /// the original unparsed value
/// needed to generate the css selector /// needed to generate the css selector
@ -23,7 +27,9 @@ impl<'a> Class<'a> {
} = self; } = self;
let mut rest = modifiers let mut rest = modifiers
.all
.iter() .iter()
.filter(|m| Responsive::from_str(*m).is_none() && ReducedMotion::from_str(*m).is_none())
.map(|m| -> &str { z.modifiers.get(*m).map(AsRef::as_ref).unwrap_or(m) }) .map(|m| -> &str { z.modifiers.get(*m).map(AsRef::as_ref).unwrap_or(m) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(":"); .join(":");
@ -56,6 +62,8 @@ impl<'a> Class<'a> {
r r
} }
/// generates the css rule for this class
/// does not generate the corresponding media query
pub(crate) fn generate(&self, z: &Zephyr) -> Result<String, ZephyrError> { pub(crate) fn generate(&self, z: &Zephyr) -> Result<String, ZephyrError> {
let property = z let property = z
.properties .properties
@ -87,4 +95,17 @@ impl<'a> Class<'a> {
Err(ZephyrError::ValueMissing) Err(ZephyrError::ValueMissing)
} }
} }
pub fn generate_with_media_query(&self, z: &Zephyr) -> Result<String, ZephyrError> {
let mut css = self.generate(z)?;
if let Some(r) = &self.modifiers.responsive {
css = r.wrap(&css);
}
if let Some(r) = &self.modifiers.reduced_motion {
css = r.wrap(&css);
}
Ok(css)
}
} }

View File

@ -6,6 +6,8 @@ use crate::{defaults::*, parse::*};
mod class; mod class;
mod defaults; mod defaults;
mod media_queries;
mod modifiers;
mod parse; mod parse;
#[cfg(feature = "inventory")] #[cfg(feature = "inventory")]
@ -97,6 +99,8 @@ impl Zephyr {
} }
}) })
// we ignore errors // we ignore errors
// TODO change this to call parse_class directly
// TODO then group by media query
.flat_map(|c| match self.generate_class(c) { .flat_map(|c| match self.generate_class(c) {
Ok(v) => Some(v), Ok(v) => Some(v),
Err(err) => { Err(err) => {
@ -116,7 +120,7 @@ impl Zephyr {
/// this one returns an error if parsing or generating fails /// this one returns an error if parsing or generating fails
pub fn generate_class(&self, class: &str) -> Result<String, ZephyrError> { pub fn generate_class(&self, class: &str) -> Result<String, ZephyrError> {
let c = parse_class(class)?; let c = parse_class(class)?;
c.generate(self) c.generate_with_media_query(self)
} }
} }
@ -235,4 +239,16 @@ mod tests {
r#".border\{1px_solid_black\}{border:1px_solid_black;}.w\{full\}{width:full;}"# r#".border\{1px_solid_black\}{border:1px_solid_black;}.w\{full\}{width:full;}"#
); );
} }
#[test]
fn generate_with_media_query() {
let z = Zephyr::new();
// the curly brackets indicate that the value should not go through replacements
let classes = z.generate_classes(["m[1rem]sm"]);
assert_eq!(
classes,
r#"@media(min-width:640px){.m\[1rem\]sm{margin:1rem;}}"#
);
}
} }

57
src/media_queries.rs Normal file
View File

@ -0,0 +1,57 @@
#[derive(PartialEq, Debug)]
pub(crate) enum Responsive {
Sm,
Md,
Lg,
Xl,
Xxl,
}
impl Responsive {
pub fn wrap(&self, css: &str) -> String {
match self {
Responsive::Sm => wrap_in_query(css, "min-width:640px"),
Responsive::Md => wrap_in_query(css, "min-width:768px"),
Responsive::Lg => wrap_in_query(css, "min-width:1024px"),
Responsive::Xl => wrap_in_query(css, "min-width:1280px"),
Responsive::Xxl => wrap_in_query(css, "min-width:1536px"),
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"sm" => Some(Responsive::Sm),
"md" => Some(Responsive::Md),
"lg" => Some(Responsive::Lg),
"xl" => Some(Responsive::Xl),
"xxl" => Some(Responsive::Xxl),
_ => None,
}
}
}
#[derive(PartialEq, Debug)]
pub enum ReducedMotion {
MotionReduce,
MotionSafe,
}
impl ReducedMotion {
pub fn wrap(&self, css: &str) -> String {
match self {
Self::MotionReduce => wrap_in_query(css, "prefers-reduced-motion:reduce"),
Self::MotionSafe => wrap_in_query(css, "prefers-reduced-motion:no-preference"),
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"motion-reduce" => Some(ReducedMotion::MotionReduce),
"motion-safe" => Some(ReducedMotion::MotionSafe),
_ => None,
}
}
}
fn wrap_in_query(css: &str, query: &str) -> String {
format!("@media({query}){{{css}}}")
}

32
src/modifiers.rs Normal file
View File

@ -0,0 +1,32 @@
use crate::media_queries::{ReducedMotion, Responsive};
#[derive(PartialEq, Debug)]
pub(crate) struct Modifiers<'a> {
pub all: Vec<&'a str>,
pub responsive: Option<Responsive>,
pub reduced_motion: Option<ReducedMotion>,
}
impl<'a> Modifiers<'a> {
pub(crate) fn new(all: Vec<&'a str>) -> Self {
let mut responsive = None;
let mut reduced_motion = None;
for m in &all {
responsive = Responsive::from_str(m);
reduced_motion = ReducedMotion::from_str(m);
}
Self {
all,
responsive,
reduced_motion,
}
}
}
impl<'a> From<Vec<&'a str>> for Modifiers<'a> {
fn from(v: Vec<&'a str>) -> Self {
Self::new(v)
}
}