From b92b16f1b8e26ca7daade320e974efe3db24815b Mon Sep 17 00:00:00 2001 From: rnentjes Date: Wed, 26 Feb 2020 19:55:10 +0100 Subject: [PATCH] Validation changes, more properties, cleanup --- build.gradle.kts | 8 ++ .../kotlin/nl/astraeus/css/CssBuilder.kt | 2 +- .../nl/astraeus/css/properties/Animation.kt | 2 +- .../nl/astraeus/css/properties/Background.kt | 46 ---------- .../nl/astraeus/css/properties/Border.kt | 25 +++--- .../nl/astraeus/css/properties/Common.kt | 36 ++++++++ .../nl/astraeus/css/properties/CssProperty.kt | 4 +- .../kotlin/nl/astraeus/css/style/Style.kt | 86 +++++++++++++++---- .../nl/astraeus/css/style/StyleDefinition.kt | 23 +++-- .../kotlin/nl/astraeus/css/style/Validator.kt | 41 +++++++++ .../kotlin/nl/astraeus/logging/Logger.kt | 5 ++ src/jvmMain/kotlin/nl/astraeus/css/Test.kt | 11 ++- 12 files changed, 200 insertions(+), 89 deletions(-) create mode 100644 src/commonMain/kotlin/nl/astraeus/css/properties/Common.kt create mode 100644 src/commonMain/kotlin/nl/astraeus/css/style/Validator.kt create mode 100644 src/commonMain/kotlin/nl/astraeus/logging/Logger.kt diff --git a/build.gradle.kts b/build.gradle.kts index c1376b5..050501e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,8 @@ kotlin { val commonMain by getting { dependencies { implementation(kotlin("stdlib-common")) + + implementation("io.github.microutils:kotlin-logging-common:1.7.8") } } val commonTest by getting { @@ -35,6 +37,8 @@ kotlin { val jsMain by getting { dependencies { implementation(kotlin("stdlib-js")) + + implementation("io.github.microutils:kotlin-logging-js:1.7.8") } } val jsTest by getting { @@ -46,6 +50,10 @@ kotlin { val jvmMain by getting { dependencies { implementation(kotlin("stdlib-jdk8")) + + implementation("org.slf4j:slf4j-api:1.7.29") + implementation("org.slf4j:slf4j-simple:1.7.29") + implementation("io.github.microutils:kotlin-logging:1.7.8") } } } diff --git a/src/commonMain/kotlin/nl/astraeus/css/CssBuilder.kt b/src/commonMain/kotlin/nl/astraeus/css/CssBuilder.kt index fe4cebd..9e5d75b 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/CssBuilder.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/CssBuilder.kt @@ -18,7 +18,7 @@ class CssBuilder { definition(this.definition) } - fun getCss(): String = definition.generateCss() + fun getCss(minified: Boolean = false): String = definition.generateCss(minified = minified) override fun toString(): String { return "CssBuilder(${definition.generateCss()})" diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/Animation.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/Animation.kt index 9e530bd..7e07961 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/Animation.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/Animation.kt @@ -69,7 +69,7 @@ class AnimationTimingFunction( n2: Double, n3: Double, n4: Double - ) = AnimationTimingFunction("cubic-bezier($n1, $n2, $n3, $n4)") + ) = AnimationTimingFunction("cubic-bezier($n1,$n2,$n3,$n4)") fun initial() = AnimationTimingFunction("initial") fun inherit() = AnimationTimingFunction("inherit") diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/Background.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/Background.kt index 1252bfe..5ed3815 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/Background.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/Background.kt @@ -31,50 +31,10 @@ class BackgroundBlendMode( } } -class BackgroundClip( - value: String -) : CssProperty(value) { - - companion object { - fun borderBox() = BackgroundClip("border-box") - fun paddingBox() = BackgroundClip("padding-box") - fun contentBox() = BackgroundClip("content-box") - fun initial() = BackgroundClip("initial") - fun inherit() = BackgroundClip("inherit") - - } -} - -class BackgroundOrigin( - value: String -) : CssProperty(value) { - - companion object { - fun borderBox() = BackgroundOrigin("border-box") - fun paddingBox() = BackgroundOrigin("padding-box") - fun contentBox() = BackgroundOrigin("content-box") - fun initial() = BackgroundOrigin("initial") - fun inherit() = BackgroundOrigin("inherit") - - } -} - class BackgroundPosition( value: String ) : CssProperty(value) { - override fun validateMultiple(props: List<*>) { - for (prop in props) { - if (prop is CssProperty) { - if (prop.css() == "initial" || prop.css() == "inherit") { - check(props.size == 1) { - "'background-position' can only have single value when 'initial' or 'inherit'" - } - } - } - } - } - companion object { fun left() = BackgroundPosition("left") fun center() = BackgroundPosition("center") @@ -106,12 +66,6 @@ class BackgroundSize( value: String ) : CssProperty(value) { - override fun validateMultiple(props: List<*>) { - check(props.size <= 2) { - "'background-size' can not have more than 2 values" - } - } - companion object { fun px(px: Int) = BackgroundSize("${px}px") fun perc(pc: Double) = BackgroundSize("${pc}%") diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/Border.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/Border.kt index 4842b92..c9b2ec4 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/Border.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/Border.kt @@ -4,21 +4,6 @@ class BorderRadius( value: String ): CssProperty(value) { - override fun validateMultiple(props: List<*>) { - check(props.size <= 2) { - "'background-size' can not have more than 2 values" - } - for (prop in props) { - if (prop is CssProperty) { - if (prop.css() == "initial" || prop.css() == "inherit") { - check(props.size == 1) { - "'border-radius' can only have single value when 'initial' or 'inherit'" - } - } - } - } - } - companion object { fun px(nr: Int) = BorderRadius("${nr}px") fun em(nr: Int) = BorderRadius("${nr}em") @@ -77,3 +62,13 @@ class BorderWidth( fun inherit() = BorderWidth("inherit") } } + +class BorderCollapse( + value: String +): CssProperty(value) { + + companion object { + fun separate() = BorderWidth("separate") + fun collapse() = BorderWidth("collapse") + } +} diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/Common.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/Common.kt new file mode 100644 index 0000000..6062ba3 --- /dev/null +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/Common.kt @@ -0,0 +1,36 @@ +package nl.astraeus.css.properties + +class Length( + value: String +): CssProperty(value) { + + companion object { + fun px(nr: Int) = Length("${nr}px") + fun em(nr: Int) = Length("${nr}em") + fun em(nr: Double) = Length("${nr}em") + fun perc(nr: Int) = Length("${nr}%") + fun perc(nr: Double) = Length("${nr}%") + fun pc(nr: Int) = Length("${nr}pc") + fun pc(nr: Double) = Length("${nr}pc") + fun cm(nr: Int) = Length("${nr}cm") + fun cm(nr: Double) = Length("${nr}cm") + fun initial() = Length("initial") + fun inherit() = Length("inherit") + } + +} + +class ClipOrigin( + value: String +) : CssProperty(value) { + + companion object { + fun borderBox() = ClipOrigin("border-box") + fun paddingBox() = ClipOrigin("padding-box") + fun contentBox() = ClipOrigin("content-box") + fun initial() = ClipOrigin("initial") + fun inherit() = ClipOrigin("inherit") + + } +} + diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt index 35fd2c9..2ad023f 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt @@ -3,11 +3,9 @@ package nl.astraeus.css.properties open class CssProperty( val value: String ) { + fun css(): String = value - open fun validate() {} - - open fun validateMultiple(props: List<*>) {} } fun text(value: String) = TextProperty(value) diff --git a/src/commonMain/kotlin/nl/astraeus/css/style/Style.kt b/src/commonMain/kotlin/nl/astraeus/css/style/Style.kt index 3ce63c2..7917894 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/style/Style.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/style/Style.kt @@ -1,6 +1,7 @@ package nl.astraeus.css.style import nl.astraeus.css.properties.* +import nl.astraeus.logging.log @DslMarker annotation class CssTagMarker @@ -25,21 +26,24 @@ open class Style( var background: TextProperty? = null, var backgroundAttachment: BackgroundAttachment? = null, var backgroundBlendMode: BackgroundBlendMode? = null, - var backgroundClip: BackgroundClip? = null, + var backgroundClip: ClipOrigin? = null, var backgroundColor: Color? = null, var backgroundImage: Image? = null, - var backgroundOrigin: BackgroundOrigin? = null, + var backgroundOrigin: ClipOrigin? = null, var backgroundPosition: List? = null, var backgroundRepeat: List? = null, var backgroundSize: List? = null, var border: TextProperty? = null, var borderBottom: TextProperty? = null, - var borderColor: Color? = null, var borderBottomColor: Color? = null, var borderBottomLeftRadius: List? = null, var borderBottomRightRadius: List? = null, var borderBottomStyle: BorderStyle? = null, var borderBottomWidth: BorderWidth? = null, + var borderCollapse: BorderCollapse? = null, + var borderColor: List? = null, + var borderImage: TextProperty? = null, + var borderImageOutset: Length? = null, var borderLeft: TextProperty? = null, var borderLeftColor: Color? = null, var borderRadius: List? = null, @@ -57,8 +61,17 @@ open class Style( var transitionDuration: DelayDuration? = null, var width: Measurement? = null ) { + private val validators = mapOf>( + "background-position" to listOf(InitialInheritSingleValue()), + "background-size" to listOf(MaxCountValidator(2)), + "border-radius" to listOf( + MaxCountValidator(4), + InitialInheritSingleValue() + ), + "animation-timing-function" to listOf(MaxCountValidator(4)) + ) - fun getMapping() = mapOf( + fun getMapping(): Map = mapOf( "align-content" to alignContent, "align-items" to alignItems, "align-self" to alignSelf, @@ -89,9 +102,15 @@ open class Style( "border-bottom-color" to borderBottomColor, "border-bottom-left-radius" to borderBottomLeftRadius, "border-bottom-right-radius" to borderBottomRightRadius, + "border-bottom-style" to borderBottomStyle, + "border-bottom-width" to borderBottomWidth, + "border-collapse" to borderCollapse, "border-color" to borderColor, + "border-image" to borderImage, + "border-image-outset" to borderImageOutset, "border-left" to borderLeft, "border-left-color" to borderLeftColor, + "border-radius" to borderRadius, "border-right" to borderRight, "border-right-color" to borderRightColor, "border-top" to borderTop, @@ -107,38 +126,75 @@ open class Style( "width" to width ) - fun propertyCss(indent: String, name: String, prop: CssProperty): String { - prop.validate() + fun propertyCss(indent: String, name: String, prop: CssProperty, minified: Boolean): String { + validators[name]?.forEach { + if (!it.validate(prop)) { + log.warn { "Validate error '$name' - ${it.getMessage(name)}" } + } + } - return "$indent$name: ${prop.css()};\n" + return if (!minified) { + val paddedName = StringBuilder() + paddedName.append(indent) + paddedName.append(name) + paddedName.append(":") + while(paddedName.length < 32) { + paddedName.append(' ') + } + "$paddedName${prop.css()};\n" + } else { + "$name:${prop.css()};" + } } - fun propertyCss(indent: String, name: String, props: List<*>): String { + fun propertyCss(indent: String, name: String, props: List<*>, minified: Boolean): String { val builder = StringBuilder() + validators[name]?.forEach { + if (!it.validate(props as List)) { + log.warn { "Validate error '$name' - ${it.getListMessage(name)}" } + } + } + for ((index, prop) in props.withIndex()) { if (prop is CssProperty) { - if (index == 0) { - prop.validateMultiple(props) + validators[name]?.forEach { + if (!it.validate(prop)) { + log.warn { "Validate error '$name' - ${it.getMessage(name)}" } + } } if (builder.isNotEmpty()) { - builder.append(", ") + builder.append(",") + if (!minified) { + builder.append(" ") + } } builder.append(prop.css()) } } - return "$indent$name: $builder;\n" + return if (!minified) { + val paddedName = StringBuilder() + paddedName.append(indent) + paddedName.append(name) + paddedName.append(":") + while(paddedName.length < 32) { + paddedName.append(' ') + } + "$paddedName$builder;\n" + } else { + "$name:$builder;" + } } - fun generatePropertyCss(indent: String): String { + fun generatePropertyCss(indent: String, minified: Boolean): String { val builder = StringBuilder() for ((name, prop) in getMapping()) { if (prop is List<*> && prop.isNotEmpty()) { - builder.append(propertyCss(indent, name, prop)) + builder.append(propertyCss(indent, name, prop, minified)) } else if (prop is CssProperty) { - builder.append(propertyCss(indent, name, prop)) + builder.append(propertyCss(indent, name, prop, minified)) } } diff --git a/src/commonMain/kotlin/nl/astraeus/css/style/StyleDefinition.kt b/src/commonMain/kotlin/nl/astraeus/css/style/StyleDefinition.kt index 05e058f..b2c621f 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/style/StyleDefinition.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/style/StyleDefinition.kt @@ -17,23 +17,32 @@ open class StyleDefinition : Style() { includes.add(style) } - open fun generateCss(namespace: String = "", indent: String = " "): String { + open fun generateCss(namespace: String = "", indent: String = " ", minified: Boolean = false): String { val builder = StringBuilder() for ((name, prop) in definitions) { val css = StringBuilder() - css.append(prop.generatePropertyCss(indent)) + css.append(prop.generatePropertyCss(indent, minified)) for (style in prop.includes) { - css.append(style.generatePropertyCss(indent)) + css.append(style.generatePropertyCss(indent, minified)) } if (css.isNotBlank()) { - builder.append("$namespace $name".trim()) - builder.append(" {\n") + builder.append("\n$namespace $name".trim()) + if (!minified) { + builder.append(" ") + } + builder.append("{") + if (!minified) { + builder.append("\n") + } builder.append(css) - builder.append("}\n\n") + builder.append("}") + if (!minified) { + builder.append("\n\n") + } } - builder.append(prop.generateCss( "${namespace} $name".trim(), indent)) + builder.append(prop.generateCss( "$namespace $name".trim(), indent, minified)) } return builder.toString() diff --git a/src/commonMain/kotlin/nl/astraeus/css/style/Validator.kt b/src/commonMain/kotlin/nl/astraeus/css/style/Validator.kt new file mode 100644 index 0000000..7769e48 --- /dev/null +++ b/src/commonMain/kotlin/nl/astraeus/css/style/Validator.kt @@ -0,0 +1,41 @@ +package nl.astraeus.css.style + +import nl.astraeus.css.properties.CssProperty + +abstract class Validator { + + open fun validate(property: CssProperty): Boolean = true + + open fun validate(properties: List): Boolean = true + + open fun getMessage(name: String): String = "'$name' validation message not defined for $this" + + open fun getListMessage(name: String): String = "'$name' validation message not defined for $this" + +} + +class MaxCountValidator( + val number: Int +): Validator() { + + override fun validate(property: List): Boolean = property.size <= number + + override fun getListMessage(name: String): String = "'$name' should not have more than 4 entries" + +} + +class InitialInheritSingleValue: Validator() { + + override fun validate(properties: List): Boolean { + for (prop in properties) { + if (prop.css() == "initial" || prop.css() == "inherit") { + return properties.size == 1 + } + } + + return true + } + + override fun getListMessage(name: String): String = "'$name' can only have single value when 'initial' or 'inherit'" + +} diff --git a/src/commonMain/kotlin/nl/astraeus/logging/Logger.kt b/src/commonMain/kotlin/nl/astraeus/logging/Logger.kt new file mode 100644 index 0000000..a55b314 --- /dev/null +++ b/src/commonMain/kotlin/nl/astraeus/logging/Logger.kt @@ -0,0 +1,5 @@ +package nl.astraeus.logging + +import mu.KotlinLogging + +val log = KotlinLogging.logger {} diff --git a/src/jvmMain/kotlin/nl/astraeus/css/Test.kt b/src/jvmMain/kotlin/nl/astraeus/css/Test.kt index 2764eff..7155406 100644 --- a/src/jvmMain/kotlin/nl/astraeus/css/Test.kt +++ b/src/jvmMain/kotlin/nl/astraeus/css/Test.kt @@ -54,7 +54,7 @@ private fun generateCss( css("a") { width = Measurement.px(725) - background = text("") + background = text("red initial") backgroundColor = base.mainBackgroundColor all = All.initial() } @@ -89,10 +89,19 @@ fun main() { animationIterationCount = listOf( Count.count(3), Count.infinite()) animationTimingFunction = listOf(AnimationTimingFunction.cubicBezier(0.1, 0.2, 0.3, 0.4), AnimationTimingFunction.easeInOut()) + borderRadius = listOf( + BorderRadius.px(4), + BorderRadius.px(5), + BorderRadius.px(6), + BorderRadius.px(7), + BorderRadius.px(8) + ) } } } println("======") println(sd.generateCss()) + println("======") + println(sd.generateCss(minified = true)) }