Skip to content

Commit

Permalink
Issue #12: Added PseudoElement and ** syntax; updated Translator; upd…
Browse files Browse the repository at this point in the history
…ated related Specs; added PlaceholderProcessor (also fixed bug in processing)
  • Loading branch information
adamldavis committed Jul 18, 2019
1 parent 286043a commit c7a84ea
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 8 deletions.
84 changes: 82 additions & 2 deletions src/main/groovy/org/groocss/GrooCSS.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ limitations under the License.
*/
package org.groocss

import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
import org.codehaus.groovy.control.CompilerConfiguration

import groovy.transform.*
Expand Down Expand Up @@ -1650,6 +1652,45 @@ class GrooCSS extends Script implements CurrentKeyFrameHolder {
final Underscore underscore = new Underscore(this)
Underscore get_() { underscore }

//---> Pseudo-elements
/** Pseudo-element ::placeholder. */
PseudoElement.StyleGroup placeholder(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('placeholder', closure)
}
/** Pseudo-element ::after. */
PseudoElement.StyleGroup after(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('after', closure)
}
/** Pseudo-element ::before. */
PseudoElement.StyleGroup before(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('before', closure)
}
/** Pseudo-element ::backdrop. */
PseudoElement.StyleGroup backdrop(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('backdrop', closure)
}
/** Pseudo-element ::cue. */
PseudoElement.StyleGroup cue(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('cue', closure)
}
/** Pseudo-element ::firstLetter. */
PseudoElement.StyleGroup firstLetter(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('first-letter', closure)
}
/** Pseudo-element ::firstLine. */
PseudoElement.StyleGroup firstLine(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('first-line', closure)
}
/** Pseudo-element ::selection. */
PseudoElement.StyleGroup selection(@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('selection', closure)
}
/** Pseudo-element ::slotted. */
PseudoElement.StyleGroup slotted(String select,
@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {
withPseudoElement('slotted(' + select + ')', closure)
}

//---> Pseudo-classes

/** Pseudo-class: :active. */
Expand Down Expand Up @@ -1793,8 +1834,7 @@ class GrooCSS extends Script implements CurrentKeyFrameHolder {
PseudoClass.StyleGroup visited(
@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) { withPseudoClass('visited', closure) }

@TypeChecked
PseudoClass.StyleGroup withPseudoClass(String pseudoClass,
PseudoClass.StyleGroup withPseudoClass(String pseudoClass,
@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {

def sg = new PseudoClass.StyleGroup(":$pseudoClass", this.config, currentCss)
Expand All @@ -1804,6 +1844,18 @@ class GrooCSS extends Script implements CurrentKeyFrameHolder {
currentCss.add sg
sg
}

PseudoElement.StyleGroup withPseudoElement(String pseudoElement,
@ClosureParams(value = SimpleType, options = "org.groocss.StyleGroup")
@DelegatesTo(value = StyleGroup, strategy = Closure.DELEGATE_FIRST) Closure closure) {

def sg = new PseudoElement.StyleGroup("::$pseudoElement", this.config, currentCss)
closure.delegate = sg
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure(sg)
currentCss.add sg
sg
}

/** Pseudo-class: :active. */
PseudoClass getActive() { newPseudoClass('active') }
Expand Down Expand Up @@ -1914,6 +1966,34 @@ class GrooCSS extends Script implements CurrentKeyFrameHolder {
new PseudoClass(value)
}

// -----> Pseudo-elements
/** Pseudo-element ::placeholder. */
PseudoElement getPlaceholder() { new PseudoElement('placeholder') }

/** Pseudo-element ::after. */
PseudoElement getAfter() { new PseudoElement('after') }

/** Pseudo-element ::before. */
PseudoElement getBefore() { new PseudoElement('before') }

/** Pseudo-element ::backdrop. */
PseudoElement getBackdrop() { new PseudoElement('backdrop') }

/** Pseudo-element ::cue. */
PseudoElement getCue() { new PseudoElement('cue') }

/** Pseudo-element ::firstLetter. */
PseudoElement getFirstLetter() { new PseudoElement('first-letter') }

/** Pseudo-element ::firstLine. */
PseudoElement getFirstLine() { new PseudoElement('first-line') }

/** Pseudo-element ::selection. */
PseudoElement getSelection() { new PseudoElement('selection') }

/** Pseudo-element ::slotted. */
PseudoElement slotted(String select) { new PseudoElement('slotted(' + select + ')') }

Raw raw(String raw) {
def r = new Raw(raw)
currentCss << r
Expand Down
6 changes: 4 additions & 2 deletions src/main/groovy/org/groocss/MediaCSS.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,18 @@ class MediaCSS implements CSSPart {
@CompileStatic
private List<String> doProcessingOf(Processor.Phase phase) {
List<String> errors = []
List<? extends CSSPart> parts = []

config.processors.forEach { Processor proc ->
List<? extends CSSPart> parts = []
Class<CSSPart> type = ((ParameterizedType) proc.class.genericInterfaces[0]).actualTypeArguments[0]

switch (type) {
case Style: groups.findAll{it instanceof StyleGroup}.each { parts.addAll(((StyleGroup)it).styleList) }
break
case StyleGroup: parts.addAll groups.findAll{it instanceof StyleGroup}; break
case Raw: parts.addAll groups.findAll{it instanceof Raw}; break
case Comment: parts.addAll groups.findAll{it instanceof Comment}; break
case MediaCSS: parts.addAll otherCss; break
case MediaCSS: parts.add(this); parts.addAll otherCss; break
case FontFace: parts.addAll fonts; break
case KeyFrames: parts.addAll kfs; break
default: parts.addAll groups; break
Expand Down
3 changes: 2 additions & 1 deletion src/main/groovy/org/groocss/PseudoClass.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ import groovy.transform.*
*
* @see GrooCSS
*/
@TypeChecked
@CompileStatic
@TupleConstructor
@EqualsAndHashCode
class PseudoClass {

/** Only here to restrict the DSL so that pseudo-class is used properly. */
Expand Down
35 changes: 35 additions & 0 deletions src/main/groovy/org/groocss/PseudoElement.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.groocss

import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors

/**
* Represents a CSS3 Pseudo-element such as "::before", "::after", "::first-letter", "::first-line",
* and "::placeholder". "::placeholder" is NOT supported by IE as of July 11 2019.
*
* @see GrooCSS
* @since 1.0-M4
*/
@InheritConstructors
@CompileStatic
class PseudoElement extends PseudoClass {

/** Only here to restrict the DSL so that pseudo-element is used properly. */
@InheritConstructors
static class StyleGroup extends org.groocss.StyleGroup {}

/** Allows this to be chainable to pseudo-classes. */
PseudoElement mod(PseudoClass other) {
new PseudoElement(this.value + "$other")
}

/** Allows this to be chainable to pseudo-classes. */
PseudoElement.StyleGroup mod(PseudoClass.StyleGroup other) {
new PseudoElement.StyleGroup(this.value + "$other", other.config, other.owner)
}

@Override
String toString() {
return "::$value"
}
}
15 changes: 13 additions & 2 deletions src/main/groovy/org/groocss/Selector.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,23 @@ class Selector extends Selectable {
String toString() { value }

/** Adds the given PseudoClass's value to this selector. Allows syntax: input % hover. */
def mod(PseudoClass pc) {
Selector mod(PseudoClass pc) {
new Selector("$value$pc", owner)
}

/** Prepends the value of this selector to the given styleGroup's selector. Allows syntax: input % hover {...}. */
def mod(PseudoClass.StyleGroup styleGroup) {
PseudoClass.StyleGroup mod(PseudoClass.StyleGroup styleGroup) {
styleGroup.resetSelector(value + styleGroup.selector)
styleGroup
}

/** Adds the given PseudoElement's value to this selector. Allows syntax: input ** before. */
Selector power(PseudoElement pc) {
new Selector("$value$pc", owner)
}

/** Prepends the value of this selector to the given styleGroup's selector. Allows syntax: input ** before {...}. */
PseudoElement.StyleGroup power(PseudoElement.StyleGroup styleGroup) {
styleGroup.resetSelector(value + styleGroup.selector)
styleGroup
}
Expand Down
9 changes: 8 additions & 1 deletion src/main/groovy/org/groocss/Translator.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Translator {
static final String STYLE_RE = /[-\w]+\s*:\s*[^\{\};]+;?\s*/
static final String FRAME_RE = /([0-9]+|from|to)%?\s*\{?\s*/
static final String FRAME_LIST_RE = /([0-9]+%,? *){2,}\s*\{?\s*/
static final String SELECTOR_RE = /[- >\~\+*#\[,:\]="\w\.\(\)]+\s*[\{,]\s*/
static final String SELECTOR_RE = /[- >\~\+*#\[,:\]{1,2}="\w\.\(\)]+\s*[\{,]\s*/

private static void processLineSwitch(String originalLine, String line, Printer pw, Map state) {
switch (line) {
Expand Down Expand Up @@ -180,6 +180,9 @@ class Translator {
else if (line ==~ /\w+:[-+\w\.\(\)]+/) { //pseudo-class
processPseudo(original.replaceAll(/ *, */, ' |'))
}
else if (line ==~ /\w+::[-+\w\.\(\)]+/) { //pseudo-element
processPseudo(original.replaceAll(/ *, */, ' |'))
}
else if (line ==~ /\w+(\.\w+)?(\s+\w+(\.\w+)?)*/) { /* Spaces separating simple elements. */
"$line $ending" // don't change a thing
}
Expand All @@ -201,10 +204,14 @@ class Translator {

static String processPseudo(String line) {
String result = line
Matcher pseudoElement = line =~ /::([-\w]+)/
Matcher pseudo = line =~ /:([-\w]+)/
Matcher parens = line =~ /\(([-+\w\.]+)\)/
boolean hasParens = parens.find()

if (pseudoElement.find())
result = result.replace( '::'+ pseudoElement.group(1), '**' + nameToCamel(pseudoElement.group(1)) )

if (pseudo.find())
result = result.replace( ':'+ pseudo.group(1), '%' + nameToCamel(pseudo.group(1)) )

Expand Down
10 changes: 10 additions & 0 deletions src/main/groovy/org/groocss/Underscore.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ class Underscore {
new Selector(".$name", grooCSS.currentCss)
}

/** Allows _** syntax for pseudo-elements like ::before and ::after. */
StyleGroup power(PseudoElement.StyleGroup styleGroup) {
return styleGroup
}

/** Allows _% syntax for pseudo-classes like :active and :hover. */
StyleGroup mod(PseudoClass.StyleGroup styleGroup) {
return styleGroup
}

def methodMissing(String name, args) {
if (args.length > 0 && args[0] instanceof Closure)
grooCSS.sg(".$name", (Closure) args[0])
Expand Down
37 changes: 37 additions & 0 deletions src/main/groovy/org/groocss/proc/PlaceholderProcessor.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.groocss.proc

import groovy.transform.CompileStatic
import org.groocss.MediaCSS
import org.groocss.StyleGroup

/**
* Finds any StyleGroup with placeholder in it and adds a copy of it with -webkit-input- prefix added to placeholder.
*/
@CompileStatic
class PlaceholderProcessor implements Processor<MediaCSS> {

static final String webkitInputPrefix = '-webkit-input-'
static final String placeholder = 'placeholder'

@Override
Optional<String> process(MediaCSS cssPart, Processor.Phase phase) {

if (phase == Processor.Phase.POST_VALIDATE) {
cssPart.groups.findAll {
it instanceof StyleGroup && ((StyleGroup) it).selector.contains('placeholder')
}.each {
println "WARNING: $placeholder not supported by IE"
def sg = (StyleGroup) it
def selector = sg.selector
def newSelector = selector.replace(placeholder, webkitInputPrefix + placeholder)

def newSg = new StyleGroup(newSelector, sg.config, sg.owner)

newSg.styleList.addAll sg.styleList

cssPart.groups << newSg
}
}
return Optional.empty()
}
}
Loading

0 comments on commit c7a84ea

Please sign in to comment.