diff --git a/spec/selector_spec.cr b/spec/selector_spec.cr index 46b7a03..18109b1 100644 --- a/spec/selector_spec.cr +++ b/spec/selector_spec.cr @@ -41,6 +41,14 @@ module CSS::SelectorSpec display :none end + rule Test && input do + display :inline + end + + rule "[data-test]" && input do + display :inline_block + end + rule input && "[data-test]" do display :none end @@ -53,6 +61,10 @@ module CSS::SelectorSpec display :none end + rule CSS::AttrSelector.new("data-test") && Test do + display :grid + end + rule div <= :before do display :flex content "Aloha!" @@ -124,6 +136,14 @@ module CSS::SelectorSpec display: none; } + input.css--selector-spec--test { + display: inline; + } + + input[data-test] { + display: inline-block; + } + input[data-test] { display: none; } @@ -136,6 +156,10 @@ module CSS::SelectorSpec display: none; } + .css--selector-spec--test[data-test] { + display: grid; + } + div:before { display: flex; content: "Aloha!"; diff --git a/src/css/combined_selector.cr b/src/css/combined_selector.cr index 3309d29..8b4f09f 100644 --- a/src/css/combined_selector.cr +++ b/src/css/combined_selector.cr @@ -8,8 +8,69 @@ module CSS def initialize(@first, @second); end def to_s(io : IO) - io << first - io << second + selectors = [first, second].reduce([] of Selector) do |acc, selector| + acc + flatten_selectors(selector) + end + + ordered = selectors.map_with_index do |selector, index| + {selector: selector, group: selector_group(selector), index: index} + end + ordered.sort_by! { |entry| {entry[:group], entry[:index]} } + + ordered.each do |entry| + io << entry[:selector] + end + end + + private def flatten_selectors(selector : Selector) : Array(Selector) + case selector + when CSS::CombinedSelector + [selector.first, selector.second].reduce([] of Selector) do |acc, item| + acc + flatten_selectors(item) + end + else + [selector] + end + end + + private def selector_group(selector : Selector) : Int32 + case selector + when CSS::AttrSelector + 2 + when CSS::AnySelector + 0 + when CSS::StringSelector + string_group(selector.string) + when CSS::PseudoclassSelector + tag_like?(selector.element_selector) ? 0 : 1 + else + 1 + end + end + + private def string_group(value : String) : Int32 + return 2 if value.starts_with?("[") + string_tag_like?(value) ? 0 : 1 + end + + private def tag_like?(selector : Selector) : Bool + case selector + when CSS::AnySelector + true + when CSS::StringSelector + string_tag_like?(selector.string) + when CSS::CombinedSelector + tag_like?(selector.first) || tag_like?(selector.second) + else + false + end + end + + private def string_tag_like?(value : String) : Bool + return false if value.empty? + + first = value[0] + first.ascii_letter? || first == '*' end end end diff --git a/src/css/pseudoclass_selector.cr b/src/css/pseudoclass_selector.cr index 3280dc1..4fbbe90 100644 --- a/src/css/pseudoclass_selector.cr +++ b/src/css/pseudoclass_selector.cr @@ -4,7 +4,7 @@ require "./nth_of_type" module CSS class PseudoclassSelector < Selector - @element_selector : Selector + getter element_selector : Selector @pseudoclass : CSS::Pseudoclass | CSS::NthOfType def initialize(@element_selector, @pseudoclass)