Skip to content

Commit f86824b

Browse files
authored
Merge pull request #100 from vyorkin-forks/feature/selector-combinators
Add more selector combinators
2 parents a2ab56f + bd25238 commit f86824b

File tree

4 files changed

+131
-12
lines changed

4 files changed

+131
-12
lines changed

src/CSS.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import CSS.Gradient (Extend, Radial, Ramp, circle, circular, closestCorner, clos
1616
import CSS.Property (class Val, Key(..), Literal(..), Prefixed(..), Value(..), cast, noCommas, plain, quote, value, (!)) as X
1717
import CSS.Render (Inline(..), Rendered, Sheet(..), collect, collect', face, feature, frame, getInline, getSheet, imp, kframe, mediaQuery, mediaType, merger, nel, predicate, properties, putInline, putStyleSheet, query', render, renderedInline, renderedSheet, rule', rules, selector, selector', selector'', sepWith) as X
1818
import CSS.Pseudo (hover) as X
19-
import CSS.Selector (Path(..), Predicate(..), Refinement(..), Selector(..), child, deep, element, star, with, (##), (**), (|>)) as X
19+
import CSS.Selector (Path(..), Predicate(..), Refinement(..), Selector(..), star, element, (|*), (|>), (|+), (&), byId, byClass, pseudo, func, attr, (@=), (^=), ($=), (*=), (~=), (|=)) as X
2020
import CSS.Size (Abs, Angle(..), Deg, Rad, Rel, Size(..), deg, em, ex, nil, pct, pt, px, rad, rem, sym, vh, vmax, vmin, vw) as X
2121
import CSS.String (class IsString, fromString) as X
2222
import CSS.Stylesheet (App(..), CSS, Feature(..), Keyframes(..), MediaQuery(..), MediaType(..), NotOrOnly(..), Rule(..), StyleM(..), fontFace, importUrl, key, keyframes, keyframesFromTo, prefixed, query, rule, runS, select, (?)) as X

src/CSS/Render.purs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module CSS.Render where
33
import Prelude
44

55
import CSS.Property (Key(..), Prefixed(..), Value(..), plain)
6-
import CSS.Selector (Path(..), Predicate(..), Refinement(..), Selector(..), with, star, element, (**), (|>))
6+
import CSS.Selector (Path(..), Predicate(..), Refinement(..), Selector(..), with, star, element, (|*), (|>))
77
import CSS.String (fromString)
88
import CSS.Stylesheet (CSS, StyleM, App(..), Feature(..), Keyframes(..), MediaQuery(..), MediaType(..), Rule(..), runS)
99
import Data.Array (null, (:), drop, sort, uncons, mapMaybe)
@@ -166,10 +166,10 @@ merger :: NonEmpty Array App -> Selector
166166
merger (NonEmpty x xs) =
167167
case x of
168168
Child s -> maybe s (\xs' -> merger xs' |> s) $ nel xs
169-
Sub s -> maybe s (\xs' -> merger xs' ** s) $ nel xs
170-
Root s -> maybe s (\xs' -> s ** merger xs') $ nel xs
169+
Sub s -> maybe s (\xs' -> merger xs' |* s) $ nel xs
170+
Root s -> maybe s (\xs' -> s |* merger xs') $ nel xs
171171
Pop i -> maybe (element "TODO") merger <<< nel <<< drop i $ x : xs
172-
Self sheetRules -> maybe (star `with` sheetRules) (\xs' -> merger xs' `with` sheetRules) $ nel xs
172+
Self sheetRules -> maybe (star `with` sheetRules) (\xs' -> merger xs' `with` sheetRules) $ nel xs
173173

174174
predicate :: Predicate -> String
175175
predicate (Id a ) = "#" <> a

src/CSS/Selector.purs

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,99 @@ derive instance ordSelector :: Ord Selector
5757
instance isStringSelector :: IsString Selector where
5858
fromString s =
5959
case take 1 s of
60-
"#" -> Selector (Refinement [Id $ drop 1 s]) Star
61-
"." -> Selector (Refinement [Class $ drop 1 s]) Star
60+
"#" -> Selector (byId $ drop 1 s) Star
61+
"." -> Selector (byClass $ drop 1 s) Star
6262
_ -> Selector (Refinement []) (Elem s)
6363

64+
-- | The star selector applies to all elements.
65+
-- | Maps to `*` in CSS.
6466
star :: Selector
6567
star = Selector (Refinement []) Star
6668

69+
-- | Select elements by name.
6770
element :: String -> Selector
6871
element e = Selector (Refinement []) (Elem e)
6972

73+
-- | The deep selector composer.
74+
-- | Maps to `sel1 sel2` in CSS.
7075
deep :: Selector -> Selector -> Selector
7176
deep a b = Selector (Refinement []) (Deep a b)
72-
infix 6 deep as **
77+
infix 6 deep as |*
7378

79+
-- | The child selector composer.
80+
-- | Maps to `sel1 > sel2` in CSS.
7481
child :: Selector -> Selector -> Selector
7582
child a b = Selector (Refinement []) (PathChild a b)
7683
infix 6 child as |>
7784

85+
-- | The adjacent selector composer.
86+
-- | Maps to `sel1 + sel2` in CSS.
87+
adjacent :: Selector -> Selector -> Selector
88+
adjacent a b = Selector (Refinement []) (Adjacent a b)
89+
infix 6 adjacent as |+
90+
91+
-- | The filter selector composer, adds a filter to a selector.
92+
-- | Maps to something like `sel#filter`, `sel.filter` or `sel:filter` in CSS,
93+
-- | depending on the filter.
7894
with :: Selector -> Refinement -> Selector
7995
with (Selector (Refinement fs) e) (Refinement ps) = Selector (Refinement (fs <> ps)) e
80-
infix 6 with as ##
96+
infix 6 with as &
97+
98+
-- | Filter elements by id.
99+
byId :: String -> Refinement
100+
byId = Refinement <<< pure <<< Id
101+
102+
-- | Filter elements by class.
103+
byClass :: String -> Refinement
104+
byClass = Refinement <<< pure <<< Class
105+
106+
-- | Filter elements by pseudo selector or pseudo class.
107+
-- | The preferred syntax is to use `:pseudo-selector` or
108+
-- | use one of the predefined ones from `CSS.Pseudo`.
109+
pseudo :: String -> Refinement
110+
pseudo = Refinement <<< pure <<< Pseudo
111+
112+
-- | Filter elements by pseudo selector functions.
113+
-- | The preferred way is to use one of the predefined functions from `CSS.Pseudo`.
114+
func :: String -> Array String -> Refinement
115+
func f = Refinement <<< pure <<< PseudoFunc f
116+
117+
-- | Filter elements based on the presence of a certain attribute.
118+
attr :: String -> Refinement
119+
attr = Refinement <<< pure <<< Attr
120+
121+
-- | Filter elements based on the presence of a
122+
-- | certain attribute with the specified value.
123+
attrVal :: String -> String -> Refinement
124+
attrVal a = Refinement <<< pure <<< AttrVal a
125+
infix 6 attrVal as @=
126+
127+
-- | Filter elements based on the presence of a certain attribute that
128+
-- | begins with the selected value.
129+
attrBegins :: String -> String -> Refinement
130+
attrBegins a = Refinement <<< pure <<< AttrBegins a
131+
infix 6 attrBegins as ^=
132+
133+
-- | Filter elements based on the presence of a certain attribute that
134+
-- | ends with the specified value.
135+
attrEnds :: String -> String -> Refinement
136+
attrEnds a = Refinement <<< pure <<< AttrEnds a
137+
infix 6 attrEnds as $=
138+
139+
-- | Filter elements based on the presence of a certain attribute that contains
140+
-- | the specified value as a substring.
141+
attrContains :: String -> String -> Refinement
142+
attrContains a = Refinement <<< pure <<< AttrContains a
143+
infix 6 attrContains as *=
144+
145+
-- | Filter elements based on the presence of a certain attribute that
146+
-- | have the specified value contained in a space separated list.
147+
attrSpace :: String -> String -> Refinement
148+
attrSpace a = Refinement <<< pure <<< AttrSpace a
149+
infix 6 attrSpace as ~=
150+
151+
-- | Filter elements based on the presence of a certain attribute that
152+
-- | have the specified value contained in a hyphen separated list.
153+
attrHyph :: String -> String -> Refinement
154+
attrHyph a = Refinement <<< pure <<< AttrHyph a
155+
infix 6 attrHyph as |=

test/Main.purs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Prelude
44

55
import Effect (Effect)
66
import Effect.Exception (error, throwException)
7-
import CSS (Rendered, Path(..), Predicate(..), Refinement(..), Selector(..), FontFaceSrc(..), FontFaceFormat(..), renderedSheet, renderedInline, fromString, selector, block, display, render, borderBox, boxSizing, contentBox, blue, color, body, a, p, px, dashed, border, inlineBlock, red, (?), (##), (|>), (**), hover, fontFaceSrc, fontStyle, deg, zIndex, textOverflow, opacity)
7+
import CSS (Rendered, Path(..), Predicate(..), Refinement(..), Selector(..), FontFaceSrc(..), FontFaceFormat(..), renderedSheet, renderedInline, fromString, selector, block, display, render, borderBox, boxSizing, contentBox, blue, color, body, a, p, px, dashed, border, inlineBlock, red, (?), (&), (|>), (|*), (|+), byId, byClass, (@=), (^=), ($=), (*=), (~=), (|=), hover, fontFaceSrc, fontStyle, deg, zIndex, textOverflow, opacity)
88
import CSS.FontStyle as FontStyle
99
import CSS.Text.Overflow as TextOverflow
1010
import Data.Maybe (Maybe(..))
@@ -48,7 +48,7 @@ withSelector :: Rendered
4848
withSelector = render do
4949
a ? do
5050
color blue
51-
a ## hover ? do
51+
a & hover ? do
5252
color red
5353

5454
childSelector :: Rendered
@@ -58,9 +58,43 @@ childSelector = render do
5858

5959
deepSelector :: Rendered
6060
deepSelector = render do
61-
p ** a ? do
61+
p |* a ? do
6262
display block
6363

64+
byClassById :: Rendered
65+
byClassById = render do
66+
a & byClass "bar" ? color red
67+
p & byId "foo" ? display block
68+
69+
attrVal :: Rendered
70+
attrVal = render do
71+
p & ("foo" @= "bar") ? display block
72+
73+
attrBegins :: Rendered
74+
attrBegins = render do
75+
p & ("foo" ^= "bar") ? display block
76+
77+
attrEnds :: Rendered
78+
attrEnds = render do
79+
p & ("foo" $= "bar") ? display block
80+
81+
attrContains :: Rendered
82+
attrContains = render do
83+
p & ("foo" *= "bar") ? display block
84+
85+
attrSpace :: Rendered
86+
attrSpace = render do
87+
p & ("foo" ~= "bar") ? display block
88+
89+
attrHyph :: Rendered
90+
attrHyph = render do
91+
p & ("foo" |= "bar") ? display block
92+
93+
adjacentSelector :: Rendered
94+
adjacentSelector = render do
95+
a |+ a ? do
96+
display inlineBlock
97+
6498
exampleFontStyle1 :: Rendered
6599
exampleFontStyle1 = render do
66100
fontStyle FontStyle.italic
@@ -112,6 +146,7 @@ main = do
112146
renderedSheet withSelector `assertEqual` Just "a { color: hsl(240.0, 100.0%, 50.0%) }\na:hover { color: hsl(0.0, 100.0%, 50.0%) }\n"
113147
renderedSheet childSelector `assertEqual` Just "p > a { z-index: 9 }\n"
114148
renderedSheet deepSelector `assertEqual` Just "p a { display: block }\n"
149+
renderedSheet adjacentSelector `assertEqual` Just "a + a { display: inline-block }\n"
115150

116151
renderedSheet nestedNodes `assertEqual` Just "#parent { display: block }\n#parent #child { display: block }\n"
117152

@@ -127,3 +162,12 @@ main = do
127162

128163
renderedInline exampleTextOverflow1 `assertEqual` Just "text-overflow: ellipsis"
129164
renderedInline exampleTextOverflow2 `assertEqual` Just "text-overflow: \"foobar\""
165+
166+
renderedSheet byClassById `assertEqual` Just "a.bar { color: hsl(0.0, 100.0%, 50.0%) }\np#foo { display: block }\n"
167+
renderedSheet attrVal `assertEqual` Just "p[foo='bar'] { display: block }\n"
168+
renderedSheet attrBegins `assertEqual` Just "p[foo^='bar'] { display: block }\n"
169+
renderedSheet attrEnds `assertEqual` Just "p[foo$='bar'] { display: block }\n"
170+
renderedSheet attrContains `assertEqual` Just "p[foo*='bar'] { display: block }\n"
171+
renderedSheet attrSpace `assertEqual` Just "p[foo~='bar'] { display: block }\n"
172+
renderedSheet attrHyph `assertEqual` Just "p[foo|='bar'] { display: block }\n"
173+

0 commit comments

Comments
 (0)