Skip to content

Commit a3684c5

Browse files
committed
Implement Selector
1 parent a0fc823 commit a3684c5

File tree

13 files changed

+196
-13
lines changed

13 files changed

+196
-13
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@devup-ui/react": patch
3+
---
4+
5+
Add selectors

.github/workflows/publish.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ jobs:
1515
concurrency:
1616
group: ${{ github.workflow }}-${{ github.ref }}
1717
cancel-in-progress: false
18+
container:
19+
image: xd009642/tarpaulin:develop-nightly
20+
options: --security-opt seccomp=unconfined
1821
steps:
1922
- name: Checkout
2023
uses: actions/checkout@v4
2124

22-
- uses: actions-rust-lang/setup-rust-toolchain@v1
23-
- name: Cargo tarpaulin
24-
run: cargo install cargo-tarpaulin
2525
- uses: pnpm/action-setup@v4
2626
name: Install pnpm
2727
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
cobertura.xml
23
build
34
dist
45
node_modules

libs/css/src/lib.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,14 @@ impl Ord for StyleSelector {
6060

6161
impl From<&str> for StyleSelector {
6262
fn from(value: &str) -> Self {
63-
if value.contains(":") {
64-
let t: Vec<_> = value.split(":").collect();
63+
if value.contains("&") {
64+
let t: Vec<_> = value.split("&").collect();
6565
if let Prefix(v) = t[0].into() {
66-
Dual(v, t[1].to_string())
66+
if t[1].is_empty() {
67+
Prefix(v)
68+
} else {
69+
Dual(v, t[1].to_string())
70+
}
6771
} else {
6872
Postfix(t[1].to_string())
6973
}
@@ -84,6 +88,8 @@ impl From<&str> for StyleSelector {
8488
))
8589
} else if value == "print" {
8690
Media("print".to_string())
91+
} else if value.ends_with(" ") {
92+
Prefix(value.trim().to_string())
8793
} else {
8894
Postfix(to_kebab_case(value))
8995
}
@@ -132,6 +138,7 @@ pub fn merge_selector(class_name: &str, selector: Option<&StyleSelector>) -> Str
132138
pub enum SelectorSeparator {
133139
Single,
134140
Double,
141+
None,
135142
}
136143
impl Display for SelectorSeparator {
137144
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
@@ -141,6 +148,7 @@ impl Display for SelectorSeparator {
141148
match self {
142149
SelectorSeparator::Single => ":",
143150
SelectorSeparator::Double => "::",
151+
SelectorSeparator::None => "",
144152
}
145153
)
146154
}
@@ -166,7 +174,9 @@ static DOUBLE_SEPARATOR: Lazy<HashSet<&str>> = Lazy::new(|| {
166174
});
167175

168176
pub fn get_selector_separator(key: &str) -> SelectorSeparator {
169-
if DOUBLE_SEPARATOR.contains(key) {
177+
if key.starts_with(":") || key.is_empty() {
178+
SelectorSeparator::None
179+
} else if DOUBLE_SEPARATOR.contains(key) {
170180
SelectorSeparator::Double
171181
} else {
172182
SelectorSeparator::Single
@@ -669,7 +679,7 @@ mod tests {
669679
);
670680

671681
assert_eq!(
672-
StyleSelector::from("themeDark:placeholder"),
682+
StyleSelector::from("themeDark&placeholder"),
673683
Dual(
674684
":root[data-theme=dark]".to_string(),
675685
"placeholder".to_string()
@@ -683,6 +693,11 @@ mod tests {
683693
StyleSelector::from("themeLight"),
684694
Prefix(":root[data-theme=light]".to_string())
685695
);
696+
697+
assert_eq!(
698+
StyleSelector::from("*[aria=disabled='true'] &:hover"),
699+
Dual("*[aria=disabled='true']".to_string(), ":hover".to_string())
700+
);
686701
}
687702

688703
#[test]
@@ -712,7 +727,7 @@ mod tests {
712727
);
713728

714729
assert_eq!(
715-
merge_selector("cls", Some(&"themeDark:hover".into()),),
730+
merge_selector("cls", Some(&"themeDark&hover".into()),),
716731
":root[data-theme=dark] .cls:hover"
717732
);
718733
}

libs/extractor/src/lib.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,4 +2143,59 @@ import {Button} from '@devup/ui'
21432143
)
21442144
.unwrap());
21452145
}
2146+
2147+
#[test]
2148+
#[serial]
2149+
fn custom_selector() {
2150+
reset_class_map();
2151+
assert_debug_snapshot!(extract(
2152+
"test.js",
2153+
r#"import {Box} from '@devup-ui/core'
2154+
<Box selectors={{
2155+
"&[aria-diabled='true']": {
2156+
opacity: 0.5
2157+
}
2158+
}} />
2159+
"#,
2160+
ExtractOption {
2161+
package: "@devup-ui/core".to_string(),
2162+
css_file: None
2163+
}
2164+
)
2165+
.unwrap());
2166+
2167+
reset_class_map();
2168+
assert_debug_snapshot!(extract(
2169+
"test.js",
2170+
r#"import {Box} from '@devup-ui/core'
2171+
<Box selectors={{
2172+
"*[aria-diabled='true'] &:hover": {
2173+
opacity: 0.5
2174+
}
2175+
}} />
2176+
"#,
2177+
ExtractOption {
2178+
package: "@devup-ui/core".to_string(),
2179+
css_file: None
2180+
}
2181+
)
2182+
.unwrap());
2183+
2184+
reset_class_map();
2185+
assert_debug_snapshot!(extract(
2186+
"test.js",
2187+
r#"import {Box} from '@devup-ui/core'
2188+
<Box selectors={{
2189+
"*[aria-diabled='true'] &": {
2190+
opacity: 0.5
2191+
}
2192+
}} />
2193+
"#,
2194+
ExtractOption {
2195+
package: "@devup-ui/core".to_string(),
2196+
css_file: None
2197+
}
2198+
)
2199+
.unwrap());
2200+
}
21462201
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
source: libs/extractor/src/lib.rs
3+
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"*[aria-diabled='true'] &:hover\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
4+
---
5+
ExtractOutput {
6+
styles: [
7+
Static(
8+
ExtractStaticStyle {
9+
property: "opacity",
10+
value: "0.5",
11+
level: 0,
12+
selector: Some(
13+
Dual(
14+
"*[aria-diabled='true']",
15+
":hover",
16+
),
17+
),
18+
basic: false,
19+
},
20+
),
21+
],
22+
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0\" />;\n",
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: libs/extractor/src/lib.rs
3+
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"*[aria-diabled='true'] &\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
4+
---
5+
ExtractOutput {
6+
styles: [
7+
Static(
8+
ExtractStaticStyle {
9+
property: "opacity",
10+
value: "0.5",
11+
level: 0,
12+
selector: Some(
13+
Prefix(
14+
"*[aria-diabled='true']",
15+
),
16+
),
17+
basic: false,
18+
},
19+
),
20+
],
21+
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0\" />;\n",
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: libs/extractor/src/lib.rs
3+
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"&[aria-diabled='true']\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
4+
---
5+
ExtractOutput {
6+
styles: [
7+
Static(
8+
ExtractStaticStyle {
9+
property: "opacity",
10+
value: "0.5",
11+
level: 0,
12+
selector: Some(
13+
Postfix(
14+
"[aria-diabled='true']",
15+
),
16+
),
17+
basic: false,
18+
},
19+
),
20+
],
21+
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0\" />;\n",
22+
}

libs/extractor/src/style_extractor.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,28 @@ pub fn extract_style_from_expression<'a>(
239239
// _ => ExtractResult::Remove,
240240
// };
241241
}
242+
if name == "selectors" {
243+
if let Expression::ObjectExpression(obj) = expression {
244+
let mut props = vec![];
245+
for p in obj.properties.iter_mut() {
246+
if let ObjectPropertyKind::ObjectProperty(ref mut o) = p {
247+
let name = o.key.name().unwrap().to_string();
248+
if let ExtractResult::ExtractStyle(mut styles) =
249+
extract_style_from_expression(
250+
ast_builder,
251+
None,
252+
&mut o.value,
253+
level,
254+
Some(name.as_str()),
255+
)
256+
{
257+
props.append(&mut styles);
258+
}
259+
}
260+
}
261+
return ExtractResult::ExtractStyle(props);
262+
}
263+
}
242264

243265
if let Some(new_selector) = name.strip_prefix("_") {
244266
return extract_style_from_expression(
@@ -248,7 +270,7 @@ pub fn extract_style_from_expression<'a>(
248270
level,
249271
Some(
250272
if let Some(selector) = selector {
251-
format!("{}:{}", selector, new_selector)
273+
format!("{}&{}", selector, new_selector)
252274
} else {
253275
new_selector.to_string()
254276
}

libs/sheet/src/lib.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,13 +418,24 @@ mod tests {
418418
"bg",
419419
0,
420420
"red",
421-
Some(&"themeDark:hover".into()),
421+
Some(&"themeDark&hover".into()),
422422
false,
423423
);
424424
assert_debug_snapshot!(sheet.create_css());
425425

426426
let mut sheet = StyleSheet::default();
427-
sheet.add_property("test", "bg", 0, "red", Some(&"wrong:hover".into()), false);
427+
sheet.add_property("test", "bg", 0, "red", Some(&"wrong&hover".into()), false);
428+
assert_debug_snapshot!(sheet.create_css());
429+
430+
let mut sheet = StyleSheet::default();
431+
sheet.add_property(
432+
"test",
433+
"bg",
434+
0,
435+
"red",
436+
Some(&"*[disabled='true'] &:hover".into()),
437+
false,
438+
);
428439
assert_debug_snapshot!(sheet.create_css());
429440
}
430441

0 commit comments

Comments
 (0)