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