Skip to content

Commit

Permalink
Solve nested with interp in selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
davesnx committed Jul 8, 2024
1 parent ceafee2 commit b448e63
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 15 deletions.
29 changes: 17 additions & 12 deletions packages/runtime/native/Emotion.ml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ let split_multiple_selectors rule_list =
|> Array.of_list

let resolve_selectors rules =
print_endline "Before moving media at top";
print_rules rules;
(* unnest takes a list of rules and unnest them into a flat list of rules *)
let rec unnest_selectors ~prefix rules =
(* multiple selectors are defined with commas: like .a, .b {}
Expand All @@ -288,23 +290,26 @@ let resolve_selectors rules =
Right [| Rule.Selector (current_selector, selector_rules) |]
| Rule.Selector (current_selector, selector_rules) ->
let is_first_level = prefix != "" in
(* TODO: Simplify this monstruosity *)
let new_prelude =
if is_first_level && starts_with_ampersand current_selector then
prefix ^ remove_first_ampersand current_selector
else if is_first_level && starts_with_dot current_selector then
prefix ^ remove_first_ampersand current_selector
else if is_first_level && starts_with_double_dot current_selector then
prefix ^ current_selector
else if is_first_level || starts_with_dot current_selector then
prefix ^ " " ^ current_selector
(* we derive the new prefix based on the current_selector and the previous "prefix" (aka the prefix added by the parent selector) *)
let new_prefix =
if is_first_level then
(* child starts with &, join them without space *)
if starts_with_ampersand current_selector then
prefix ^ remove_first_ampersand current_selector
(* child starts with dot, join them without space *)
else if starts_with_double_dot current_selector then
prefix ^ current_selector
(* This case is the same as the "else", but I keep it for reference *)
else if starts_with_dot current_selector then
prefix ^ " " ^ current_selector
else prefix ^ " " ^ current_selector
else prefix ^ current_selector
in
let selector_rules = split_multiple_selectors selector_rules in
let selectors, rest_of_declarations =
unnest_selectors ~prefix:new_prelude selector_rules
unnest_selectors ~prefix:new_prefix selector_rules
in
let new_selector = Rule.Selector (new_prelude, selectors) in
let new_selector = Rule.Selector (new_prefix, selectors) in
Right
(Array.append [| new_selector |] (Array.flatten rest_of_declarations))
| _ as rule -> Left rule)
Expand Down
62 changes: 59 additions & 3 deletions packages/runtime/test/test_styles.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[@@@warning "-32"]

let get_string_style_rules () =
let content = CssJs.get_stylesheet () in
let _ = CssJs.flush () in
Expand Down Expand Up @@ -453,7 +455,7 @@ let selectors_with_coma_simple =
in
let css = get_string_style_rules () in
assert_string css
(Printf.sprintf ".%s .a { top: 50px; } .%s .b { top: 50px; }" classname
(Printf.sprintf ".%s .a { top: 50px; } .%s .b { top: 50px; }" classname
classname)

let selectors_with_coma_and_pseudo =
Expand Down Expand Up @@ -664,6 +666,59 @@ let mq_and_selectors_2 =
768px) { .%s div a { height: auto; } }"
classname classname classname)

let selector_nested_inerpolation_with_multiple =
test "selector_nested_inerpolation_with_multiple" @@ fun () ->
let px24 = CssJs.px 24 in
let languageIcon =
[%cx {|
vertical-align: middle;
margin: 0.5em 0;
opacity: 0.6;
|}]
in
let menuOpened = [%cx {||}] in
let classname =
[%cx
{|
margin-right: $(px24);

&:hover, .$(menuOpened) {
.$(languageIcon) {
opacity: 1.0;
}
}
|}]
in
let css = get_string_style_rules () in
assert_string css
(Printf.sprintf
".%s { vertical-align: middle; margin: 0.5em 0; opacity: 0.6; } .%s { \
margin-right: 24px; } .%s:hover .%s { opacity: 1; } .%s .%s .%s { \
opacity: 1; }"
languageIcon classname classname languageIcon classname menuOpened
languageIcon)

let selector_nested_inerpolation_with_multiple =
test "selector_nested_inerpolation_with_multiple" @@ fun () ->
let languageIcon = [%cx {| opacity: 0.6; |}] in
let menuOpened = [%cx {||}] in
let classname =
[%cx
{|
&:hover, .$(menuOpened) {
&.$(languageIcon) {
opacity: 1.0;
}
}
|}]
in
let css = get_string_style_rules () in
assert_string css
(Printf.sprintf
".%s { opacity: 0.6; } .%s:hover.%s { opacity: 1; } .%s .%s.%s { \
opacity: 1; }"
languageIcon classname languageIcon classname menuOpened languageIcon)

let selector_with_classname_and_mq =
test "selector_with_classname_and_mq" @@ fun () ->
let nested_classname = CssJs.style [||] in
Expand All @@ -679,7 +734,7 @@ let selector_with_classname_and_mq =
let css = get_string_style_rules () in
assert_string css
(Printf.sprintf
".%s { display: block; } @media (min-width: 768px) { .%s .lola .%s { \
".%s { display: block; } @media (min-width: 768px) { .%s .lola .%s { \
height: auto; } }"
classname classname nested_classname)

Expand All @@ -700,7 +755,7 @@ let mq_with_selectors =
assert_string css
(Printf.sprintf
".%s { display: block; } @media (min-width: 768px) { .%s { height: \
auto; } .%s .lola { color: transparent; } }"
auto; } .%s .lola { color: transparent; } }"
classname classname classname)

let mq_nested =
Expand Down Expand Up @@ -984,6 +1039,7 @@ let tests =
selector_nested_with_pseudo_2;
selector_nested_with_pseudo_3;
selector_nested_with_mq_and_declarations;
selector_nested_inerpolation_with_multiple;
mq_with_selectors;
mq;
mq_nested;
Expand Down

0 comments on commit b448e63

Please sign in to comment.