media-query ranges, group queries on same class
This commit is contained in:
parent
871fce954d
commit
4cdb7919b2
15
src/class.rs
15
src/class.rs
|
@ -1,7 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
media_queries::{ReducedMotion, Responsive},
|
||||
media_queries::{wrap_in_query, ReducedMotion, Responsive},
|
||||
modifiers::Modifiers,
|
||||
Zephyr, ZephyrError,
|
||||
};
|
||||
|
@ -74,6 +74,8 @@ impl<'a> Class<'a> {
|
|||
.replace('$', "\\$")
|
||||
.replace('\'', "\\'")
|
||||
.replace('*', "\\*")
|
||||
.replace('<', "\\<")
|
||||
.replace('@', "\\@")
|
||||
.replace('%', "\\%");
|
||||
r.insert(0, '.');
|
||||
r
|
||||
|
@ -119,16 +121,19 @@ impl<'a> Class<'a> {
|
|||
}
|
||||
|
||||
pub fn generate_with_media_query(&self, z: &Zephyr) -> Result<String, ZephyrError> {
|
||||
let mut css = self.generate(z)?;
|
||||
let css = self.generate(z)?;
|
||||
|
||||
dbg!(&self.modifiers);
|
||||
|
||||
let mut queries: Vec<String> = vec![];
|
||||
if let Some(r) = &self.modifiers.responsive {
|
||||
css = r.wrap(&css);
|
||||
queries.extend(r.queries());
|
||||
}
|
||||
if let Some(r) = &self.modifiers.reduced_motion {
|
||||
css = r.wrap(&css);
|
||||
queries.extend(r.queries().iter().map(ToString::to_string));
|
||||
}
|
||||
|
||||
Ok(css)
|
||||
Ok(wrap_in_query(css, &queries))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||
use crate::SpecialDeclaration;
|
||||
|
||||
fn vec_to_hashmap(v: &[(&str, &str)]) -> HashMap<String, String> {
|
||||
v.into_iter()
|
||||
v.iter()
|
||||
.map(|(a, b)| (a.to_string(), b.to_string()))
|
||||
.collect::<HashMap<_, _>>()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) enum Responsive {
|
||||
pub(crate) struct Responsive {
|
||||
breakpoint: Breakpoint,
|
||||
range: Range,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) enum Breakpoint {
|
||||
Sm,
|
||||
Md,
|
||||
Lg,
|
||||
|
@ -7,27 +13,87 @@ pub(crate) enum Responsive {
|
|||
Xxl,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Default)]
|
||||
pub(crate) enum Range {
|
||||
#[default]
|
||||
Gte,
|
||||
Lt,
|
||||
Exact,
|
||||
}
|
||||
|
||||
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 queries(&self) -> Vec<String> {
|
||||
match self.range {
|
||||
Range::Gte => vec![format!("min-width:{}px", self.breakpoint.width())],
|
||||
Range::Lt => vec![format!("max-width:{}.9px", self.breakpoint.width() - 1)],
|
||||
Range::Exact => {
|
||||
if let Some(n) = self.breakpoint.next() {
|
||||
vec![
|
||||
format!("min-width:{}px", self.breakpoint.width()),
|
||||
format!("max-width:{}.9px", n.width() - 1),
|
||||
]
|
||||
} else {
|
||||
vec![format!("min-width:{}px", self.breakpoint.width())]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(p) = s.strip_prefix('<') {
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::from_str(p)?,
|
||||
range: Range::Lt,
|
||||
})
|
||||
} else if let Some(p) = s.strip_prefix('@') {
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::from_str(p)?,
|
||||
range: Range::Exact,
|
||||
})
|
||||
} else {
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::from_str(s)?,
|
||||
range: Range::Gte,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Breakpoint {
|
||||
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),
|
||||
"sm" => Some(Self::Sm),
|
||||
"md" => Some(Self::Md),
|
||||
"lg" => Some(Self::Lg),
|
||||
"xl" => Some(Self::Xl),
|
||||
"xxl" => Some(Self::Xxl),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> u16 {
|
||||
match self {
|
||||
Breakpoint::Sm => 640,
|
||||
Breakpoint::Md => 768,
|
||||
Breakpoint::Lg => 1024,
|
||||
Breakpoint::Xl => 1280,
|
||||
Breakpoint::Xxl => 1536,
|
||||
}
|
||||
}
|
||||
|
||||
const fn next(&self) -> Option<Self> {
|
||||
match self {
|
||||
Breakpoint::Sm => Some(Breakpoint::Md),
|
||||
Breakpoint::Md => Some(Breakpoint::Lg),
|
||||
Breakpoint::Lg => Some(Breakpoint::Xl),
|
||||
Breakpoint::Xl => Some(Breakpoint::Xxl),
|
||||
Breakpoint::Xxl => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
@ -37,10 +103,10 @@ pub enum ReducedMotion {
|
|||
}
|
||||
|
||||
impl ReducedMotion {
|
||||
pub fn wrap(&self, css: &str) -> String {
|
||||
pub fn queries(&self) -> &[&str] {
|
||||
match self {
|
||||
Self::MotionReduce => wrap_in_query(css, "prefers-reduced-motion:reduce"),
|
||||
Self::MotionSafe => wrap_in_query(css, "prefers-reduced-motion:no-preference"),
|
||||
Self::MotionReduce => &["prefers-reduced-motion:reduce"],
|
||||
Self::MotionSafe => &["prefers-reduced-motion:no-preference"],
|
||||
}
|
||||
}
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
|
@ -52,6 +118,70 @@ impl ReducedMotion {
|
|||
}
|
||||
}
|
||||
|
||||
fn wrap_in_query(css: &str, query: &str) -> String {
|
||||
format!("@media({query}){{{css}}}")
|
||||
pub(crate) fn wrap_in_query(css: String, queries: &[String]) -> String {
|
||||
if queries.is_empty() {
|
||||
return css;
|
||||
}
|
||||
let query = queries
|
||||
.iter()
|
||||
.map(|s| format!("({s})"))
|
||||
.collect::<Vec<String>>()
|
||||
.join("and");
|
||||
format!("@media{query}{{{css}}}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_responsive() {
|
||||
let r = Responsive::from_str("<lg");
|
||||
assert_eq!(
|
||||
r,
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::Lg,
|
||||
range: Range::Lt
|
||||
})
|
||||
);
|
||||
|
||||
let r = Responsive::from_str("@xl");
|
||||
assert_eq!(
|
||||
r,
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::Xl,
|
||||
range: Range::Exact
|
||||
})
|
||||
);
|
||||
|
||||
let r = Responsive::from_str("@sm");
|
||||
assert_eq!(
|
||||
r,
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::Sm,
|
||||
range: Range::Exact
|
||||
})
|
||||
);
|
||||
|
||||
let r = Responsive::from_str("xxl");
|
||||
assert_eq!(
|
||||
r,
|
||||
Some(Responsive {
|
||||
breakpoint: Breakpoint::Xxl,
|
||||
range: Range::Gte
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_queries() {
|
||||
let r = Responsive::from_str("<lg").unwrap().queries();
|
||||
assert_eq!(r, &["max-width:1023.9px"]);
|
||||
|
||||
let r = Responsive::from_str("@xl").unwrap().queries();
|
||||
assert_eq!(r, &["min-width:1280px", "max-width:1535.9px"]);
|
||||
|
||||
let r = ReducedMotion::MotionReduce.queries();
|
||||
assert_eq!(r, &["prefers-reduced-motion:reduce"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ impl<'a> Modifiers<'a> {
|
|||
let mut reduced_motion = None;
|
||||
|
||||
for m in &all {
|
||||
responsive = Responsive::from_str(m);
|
||||
reduced_motion = ReducedMotion::from_str(m);
|
||||
responsive = responsive.or_else(|| Responsive::from_str(m));
|
||||
reduced_motion = reduced_motion.or_else(|| ReducedMotion::from_str(m));
|
||||
}
|
||||
|
||||
Self {
|
||||
|
|
18
src/tests.rs
18
src/tests.rs
|
@ -121,6 +121,24 @@ fn generate_with_media_query() {
|
|||
classes,
|
||||
r#"@media(min-width:640px){.m\[1rem\]sm{margin:1rem}}"#
|
||||
);
|
||||
|
||||
let classes = z.generate_classes(["m[1rem]<md"]);
|
||||
assert_eq!(
|
||||
classes,
|
||||
r#"@media(max-width:767.9px){.m\[1rem\]\<md{margin:1rem}}"#
|
||||
);
|
||||
|
||||
let classes = z.generate_classes(["m[1rem]motion-reduce"]);
|
||||
assert_eq!(
|
||||
classes,
|
||||
r#"@media(prefers-reduced-motion:reduce){.m\[1rem\]motion-reduce{margin:1rem}}"#
|
||||
);
|
||||
|
||||
let classes = z.generate_classes(["m[1rem]@xl,motion-reduce"]);
|
||||
assert_eq!(
|
||||
classes,
|
||||
r#"@media(min-width:1280px)and(max-width:1535.9px)and(prefers-reduced-motion:reduce){.m\[1rem\]\@xl,motion-reduce{margin:1rem}}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in New Issue