Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/plenty-melons-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@devup-ui/wasm": patch
---

Fix compound selector issue
79 changes: 58 additions & 21 deletions libs/css/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,21 @@ impl Ord for StyleSelector {

impl From<&str> for StyleSelector {
fn from(value: &str) -> Self {
if let Some(s) = value.strip_prefix("group") {
Dual("*[role=group]".to_string(), to_kebab_case(s))
if value.contains(":") {
let t: Vec<_> = value.split(":").collect();
if let Prefix(v) = t[0].into() {
Dual(v, t[1].to_string())
} else {
Postfix(t[1].to_string())
}
} else if let Some(s) = value.strip_prefix("group") {
let post = to_kebab_case(s);
Prefix(format!(
"{}{}{}",
"*[role=group]",
get_selector_separator(&post),
post
))
} else if let Some(s) = value.strip_prefix("theme") {
// first character should lower case
Prefix(format!(
Expand Down Expand Up @@ -95,15 +108,20 @@ impl Display for StyleSelector {
pub fn merge_selector(class_name: &str, selector: Option<&StyleSelector>) -> String {
if let Some(selector) = selector {
match selector {
Postfix(postfix) => match get_selector_separator(postfix) {
SelectorSeparator::Single => format!(".{}:{}", class_name, postfix),
SelectorSeparator::Double => format!(".{}::{}", class_name, postfix),
},
Postfix(postfix) => format!(
".{}{}{}",
class_name,
get_selector_separator(postfix),
postfix
),
Prefix(prefix) => format!("{} .{}", prefix, class_name),
Dual(prefix, postfix) => match get_selector_separator(postfix) {
SelectorSeparator::Single => format!("{}:{} .{}", prefix, postfix, class_name),
SelectorSeparator::Double => format!("{}::{} .{}", prefix, postfix, class_name),
},
Dual(prefix, postfix) => format!(
"{} .{}{}{}",
prefix,
class_name,
get_selector_separator(postfix),
postfix
),
Media(_) => format!(".{}", class_name),
}
} else {
Expand All @@ -115,6 +133,18 @@ pub enum SelectorSeparator {
Single,
Double,
}
impl Display for SelectorSeparator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
SelectorSeparator::Single => ":",
SelectorSeparator::Double => "::",
}
)
}
}

static DOUBLE_SEPARATOR: Lazy<HashSet<&str>> = Lazy::new(|| {
let mut set = HashSet::new();
Expand Down Expand Up @@ -627,15 +657,23 @@ mod tests {
);
assert_eq!(
StyleSelector::from("groupHover"),
Dual("*[role=group]".to_string(), "hover".to_string())
Prefix("*[role=group]:hover".to_string())
);
assert_eq!(
StyleSelector::from("groupFocusVisible"),
Dual("*[role=group]".to_string(), "focus-visible".to_string())
Prefix("*[role=group]:focus-visible".to_string())
);
assert_eq!(
StyleSelector::from("group1"),
Dual("*[role=group]".to_string(), "1".to_string())
Prefix("*[role=group]:1".to_string())
);

assert_eq!(
StyleSelector::from("themeDark:placeholder"),
Dual(
":root[data-theme=dark]".to_string(),
"placeholder".to_string()
)
);

assert_eq!(Prefix(".cls".to_string()).to_string(), "-.cls-");
Expand All @@ -661,22 +699,21 @@ mod tests {
assert_eq!(
merge_selector(
"cls",
Some(&Dual(
":root[data-theme=dark]".to_string(),
"hover".to_string()
)),
Some(&Prefix(":root[data-theme=dark]:hover".to_string(),)),
),
":root[data-theme=dark]:hover .cls"
);
assert_eq!(
merge_selector(
"cls",
Some(&Dual(
":root[data-theme=dark]".to_string(),
"placeholder".to_string()
)),
Some(&Prefix(":root[data-theme=dark]::placeholder".to_string())),
),
":root[data-theme=dark]::placeholder .cls"
);

assert_eq!(
merge_selector("cls", Some(&"themeDark:hover".into()),),
":root[data-theme=dark] .cls:hover"
);
}
}
29 changes: 29 additions & 0 deletions libs/extractor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2088,4 +2088,33 @@ import {Button} from '@devup/ui'
)
.unwrap());
}

#[test]
#[serial]
fn theme_selector() {
reset_class_map();
assert_debug_snapshot!(extract(
"test.js",
r#"import {Box} from '@devup-ui/core'
<Box _themeDark={{ _hover:{bg:"black"} }} />
"#,
ExtractOption {
package: "@devup-ui/core".to_string(),
css_file: None
}
)
.unwrap());
reset_class_map();
assert_debug_snapshot!(extract(
"test.js",
r#"import {Box} from '@devup-ui/core'
<Box _hover={{bg:"white"}} _themeDark={{ _hover:{bg:"black"} }} />
"#,
ExtractOption {
package: "@devup-ui/core".to_string(),
css_file: None
}
)
.unwrap());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ ExtractOutput {
value: "red",
level: 0,
selector: Some(
Dual(
"*[role=group]",
"hover",
Prefix(
"*[role=group]:hover",
),
),
basic: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
source: libs/extractor/src/lib.rs
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box _hover={{bg:\"white\"}} _themeDark={{ _hover:{bg:\"black\"} }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
---
ExtractOutput {
styles: [
Static(
ExtractStaticStyle {
property: "background",
value: "white",
level: 0,
selector: Some(
Postfix(
"hover",
),
),
basic: false,
},
),
Static(
ExtractStaticStyle {
property: "background",
value: "black",
level: 0,
selector: Some(
Dual(
":root[data-theme=dark]",
"hover",
),
),
basic: false,
},
),
],
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0 d1\" />;\n",
}
23 changes: 23 additions & 0 deletions libs/extractor/src/snapshots/extractor__tests__theme_selector.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
source: libs/extractor/src/lib.rs
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box _themeDark={{ _hover:{bg:\"black\"} }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
---
ExtractOutput {
styles: [
Static(
ExtractStaticStyle {
property: "background",
value: "black",
level: 0,
selector: Some(
Dual(
":root[data-theme=dark]",
"hover",
),
),
basic: false,
},
),
],
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0\" />;\n",
}
15 changes: 13 additions & 2 deletions libs/extractor/src/style_extractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ pub fn extract_style_from_expression<'a>(
level: u8,
selector: Option<&str>,
) -> ExtractResult<'a> {
println!(
"extract_style_from_expression: {:?} {:?} {:?}",
selector, name, expression
);
let mut typo = false;

if name.is_none() && selector.is_none() {
Expand Down Expand Up @@ -240,13 +244,20 @@ pub fn extract_style_from_expression<'a>(
// };
}

if let Some(selector) = name.strip_prefix("_") {
if let Some(new_selector) = name.strip_prefix("_") {
return extract_style_from_expression(
ast_builder,
None,
expression,
level,
Some(selector),
Some(
if let Some(selector) = selector {
format!("{}:{}", selector, new_selector)
} else {
new_selector.to_string()
}
.as_str(),
),
);
}
typo = name == "typography";
Expand Down
46 changes: 33 additions & 13 deletions libs/sheet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,36 +188,35 @@ impl StyleSheet {
.unwrap_or_else(|| self.theme.break_points.last().cloned().unwrap_or(0)),
)
};
for (media, props) in medias {
let inner_css = props
if !sorted_props.is_empty() {
let inner_css = sorted_props
.into_iter()
.map(ExtractStyle::extract)
.collect::<Vec<String>>()
.join("");
css.push_str(
if let Some(break_point) = break_point {
format!(
"\n@media (min-width:{}px) and {}{{{}}}",
break_point, media, inner_css
)
format!("\n@media (min-width:{}px){{{}}}", break_point, inner_css)
} else {
format!("\n@media {}{{{}}}", media, inner_css.as_str())
inner_css
}
.as_str(),
);
}

if !sorted_props.is_empty() {
let inner_css = sorted_props
for (media, props) in medias {
let inner_css = props
.into_iter()
.map(ExtractStyle::extract)
.collect::<Vec<String>>()
.join("");
css.push_str(
if let Some(break_point) = break_point {
format!("\n@media (min-width:{}px){{{}}}", break_point, inner_css)
format!(
"\n@media (min-width:{}px) and {}{{{}}}",
break_point, media, inner_css
)
} else {
inner_css
format!("\n@media {}{{{}}}", media, inner_css.as_str())
}
.as_str(),
);
Expand Down Expand Up @@ -400,7 +399,7 @@ mod tests {
"bg",
0,
"red",
Some(&StyleSelector::Dual("*".to_string(), "hover".to_string())),
Some(&StyleSelector::Prefix("*:hover".to_string())),
false,
);
sheet.add_property(
Expand All @@ -412,6 +411,21 @@ mod tests {
false,
);
assert_debug_snapshot!(sheet.create_css());

let mut sheet = StyleSheet::default();
sheet.add_property(
"test",
"bg",
0,
"red",
Some(&"themeDark:hover".into()),
false,
);
assert_debug_snapshot!(sheet.create_css());

let mut sheet = StyleSheet::default();
sheet.add_property("test", "bg", 0, "red", Some(&"wrong:hover".into()), false);
assert_debug_snapshot!(sheet.create_css());
}

#[test]
Expand Down Expand Up @@ -454,6 +468,12 @@ mod tests {
sheet.add_property("test", "mx", 1, "40px", Some(&"print".into()), false);
sheet.add_property("test", "my", 1, "40px", Some(&"print".into()), false);
assert_debug_snapshot!(sheet.create_css());

let mut sheet = StyleSheet::default();
sheet.add_property("test", "mx", 0, "40px", Some(&"print".into()), false);
sheet.add_property("test", "my", 0, "40px", None, false);

assert_debug_snapshot!(sheet.create_css());
}

#[test]
Expand Down
5 changes: 5 additions & 0 deletions libs/sheet/src/snapshots/sheet__tests__create_css-8.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: libs/sheet/src/lib.rs
expression: sheet.create_css()
---
":root[data-theme=dark] .test:hover{background:red}"
5 changes: 5 additions & 0 deletions libs/sheet/src/snapshots/sheet__tests__create_css-9.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: libs/sheet/src/lib.rs
expression: sheet.create_css()
---
".test:hover{background:red}"
5 changes: 5 additions & 0 deletions libs/sheet/src/snapshots/sheet__tests__print_selector-2.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: libs/sheet/src/lib.rs
expression: sheet.create_css()
---
".test{margin-top:40px;margin-bottom:40px;}\n@media print{.test{margin-left:40px;margin-right:40px;}}"