Add option to overwrite and redeclaration warning option

This commit is contained in:
2020-09-08 09:39:15 +02:00
parent f23d54d8c8
commit dedbce0fa0
12 changed files with 259 additions and 482 deletions

View File

@@ -1,8 +1,45 @@
package nl.astraeus.css.properties
class Color(
value: String
value: String,
val readOnly: Boolean = true
) : CssProperty(value) {
/* val hue: Int? = null
val saturation: Int? = null
val lightness: Int? = null
val alpha: Double? = null
init {
}
override fun css(): String {
return super.css()
}
fun adjust(
red: Int? = null,
green: Int? = null,
blue: Int? = null,
hue: Int? = null,
saturation: Int? = null,
lightness: Int? = null,
alpha: Double? = null
): Color {
return this
}
fun darken(): Color {
if (readOnly) {
return this
} else {
return this
}
}
fun lighten(): Color {
return this
}*/
companion object {
val auto = Color("auto")
@@ -25,12 +62,21 @@ class Color(
hue: Int,
saturation: Int,
lightness: Int
) = Color("hsl($hue, $saturation, $lightness)")
) = Color("hsl($hue, $saturation%, $lightness%)")
fun hsla(
hue: Int,
saturation: Int,
lightness: Int,
alpha: Double
) = Color("hsla($hue, $saturation, $lightness, $alpha)")
) = Color("hsla($hue, $saturation%, $lightness%, $alpha)")
private fun fromHex(
red: Int,
green: Int,
blue: Int
) {
}
}
}

View File

@@ -0,0 +1,11 @@
package nl.astraeus.css.style
import nl.astraeus.css.properties.Color
object CssFunctions {
fun darken(color: Color, percentage: Int): Color {
return color
}
}

View File

@@ -1,7 +1,6 @@
package nl.astraeus.css.style
import nl.astraeus.css.properties.*
import nl.astraeus.logging.log
typealias Css = Style.() -> Unit
@@ -31,53 +30,53 @@ private fun prp(vararg css: String): List<CssProperty> {
}
abstract class CssGenerator {
val definitions: MutableMap<String, Css> = mutableMapOf()
val definitions: MutableMap<String, MutableList<Css>> = mutableMapOf()
val props: MutableMap<String, List<CssProperty>> = mutableMapOf()
abstract fun getValidator(name: String): List<Validator>?
private fun propertyCss(indent: String, name: String, props: List<CssProperty>, minified: Boolean): String {
private fun propertyCss(indent: String, name: String, props: List<CssProperty>): String {
val builder = StringBuilder()
getValidator(name)?.forEach {
if (!it.validate(props)) {
log.warn { "Validate error '$name' - ${it.getMessage(name)}" }
println( "Validate error '$name' - ${it.getMessage(name)}")
}
}
for (prop in props) {
if (builder.isNotEmpty()) {
builder.append(",")
indent(minified, builder, " ")
builder.append(" ")
}
builder.append(prop.css())
}
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;"
val paddedName = StringBuilder()
paddedName.append(indent)
paddedName.append(name)
paddedName.append(":")
while (paddedName.length < 32) {
paddedName.append(' ')
}
return "$paddedName$builder;\n"
}
fun generatePropertyCss(indent: String, minified: Boolean): String {
fun generatePropertyCss(indent: String): String {
val builder = StringBuilder()
for ((name, prop) in props) {
builder.append(propertyCss(indent, name, prop, minified))
builder.append(propertyCss(indent, name, prop))
}
return builder.toString()
}
open fun generateCss(namespace: String = "", indent: String = "", minified: Boolean = false): String {
open fun generateCss(
namespace: String = "",
indent: String = "",
minified: Boolean = false,
warnOnRedeclaration: Boolean = true
): String {
val builder = StringBuilder()
if (this is ConditionalStyle) {
@@ -85,22 +84,20 @@ abstract class CssGenerator {
mq.keys.sorted().forEach { mediaName ->
val css = mq[mediaName]
indent(minified, builder, indent)
builder.append(indent)
builder.append("@media ")
builder.append(mediaName)
builder.append(" {")
indent(minified, builder, "\n")
builder.append(" {\n")
css?.let { css ->
val mediaStyle = ConditionalStyle()
css(mediaStyle)
builder.append(mediaStyle.generateCss("", " $indent", minified))
builder.append(mediaStyle.generateCss("", " $indent"))
}
indent(minified, builder, indent)
builder.append("}")
indent(minified, builder, "\n")
builder.append(indent)
builder.append("}\n")
}
}
@@ -108,102 +105,107 @@ abstract class CssGenerator {
mq.keys.sorted().forEach { mediaName ->
val css = mq[mediaName]
indent(minified, builder, indent)
builder.append(indent)
builder.append("@supports ")
builder.append(mediaName)
builder.append(" {")
indent(minified, builder, "\n")
builder.append(" {\n")
css?.let { css ->
val mediaStyle = ConditionalStyle()
css(mediaStyle)
builder.append(mediaStyle.generateCss(""," $indent", minified))
builder.append(mediaStyle.generateCss(""," $indent"))
}
indent(minified, builder, indent)
builder.append("}")
indent(minified, builder, "\n")
builder.append(indent)
builder.append("}\n")
}
}
}
for ((name, prop) in definitions) {
for (name in definitions.keys.sorted()) {
val props = definitions[name]!!
val css = StringBuilder()
if (warnOnRedeclaration && props.size > 1) {
css.append("/* style '$name' is defined ${props.size} times! */\n")
}
val finalStyle = Style()
prop(finalStyle)
for (prop in props) {
prop(finalStyle)
}
css.append(finalStyle.generatePropertyCss(" $indent", minified))
css.append(finalStyle.generatePropertyCss(" $indent"))
if (css.isNotBlank()) {
builder.append("\n$namespace $name".trim())
indent(minified, builder, " $indent")
builder.append("{")
indent(minified, builder, "\n")
if (name.startsWith(":")) {
builder.append("\n$namespace$name".trim())
} else {
builder.append("\n$namespace $name".trim())
}
builder.append(" $indent")
builder.append("{\n")
finalStyle.fontFace?.let { ff ->
indent(minified, builder, " $indent")
builder.append("@font-face {")
indent(minified, builder, "\n")
builder.append(ff.generatePropertyCss( " $indent", minified))
builder.append(" $indent")
builder.append("}")
indent(minified, builder, "\n")
builder.append("@font-face {\n")
builder.append(ff.generatePropertyCss( " $indent"))
builder.append(" $indent")
builder.append("}\n")
}
finalStyle.keyFrames.let { kf ->
kf.keys.sorted().forEach { frameName ->
val css = kf[frameName]
indent(minified, builder, " $indent")
builder.append(" $indent")
builder.append("@keyframes ")
builder.append(frameName)
builder.append(" {")
indent(minified, builder, "\n")
builder.append(" {\n")
css?.let { css ->
for ((nr, style) in css.frames) {
indent(minified, builder, " $indent")
builder.append(" $indent")
builder.append("${nr}% ")
indent(minified, builder, " $indent")
builder.append("{")
indent(minified, builder, "\n")
builder.append(" $indent")
builder.append("{\n")
val finalStyle = Style()
style(finalStyle)
builder.append(finalStyle.generatePropertyCss(" $indent", minified))
builder.append(finalStyle.generatePropertyCss(" $indent"))
indent(minified, builder, " $indent")
builder.append("}")
indent(minified, builder, "\n")
builder.append(" $indent")
builder.append("}\n")
}
indent(minified, builder, " $indent")
builder.append("}")
indent(minified, builder, "\n")
builder.append(" $indent")
builder.append("}\n")
}
}
}
builder.append(css)
builder.append("}")
indent(minified, builder, "\n\n")
builder.append("}\n\n")
}
builder.append(finalStyle.generateCss( "$namespace $name".trim(), indent, minified))
builder.append(finalStyle.generateCss("$namespace $name".trim(), indent))
}
return builder.toString()
}
private fun indent(minified: Boolean, builder: StringBuilder, indent: String) {
if (!minified) {
builder.append(indent)
return if (minified) {
val stripped = StringBuilder()
val skip = arrayOf(' ', '\t', '\n', '\r')
for (char in builder) {
if (!skip.contains(char)) {
stripped.append(char)
}
}
stripped.toString();
} else {
builder.toString()
}
}
}
@@ -220,6 +222,8 @@ open class Style : CssGenerator() {
MaxCountValidator(4),
InitialInheritSingleValue()
),
"animation-iteration-mode" to listOf(MaxCountValidator(4)),
"animation-timing-function" to listOf(MaxCountValidator(4)),
"border-image-repeat" to listOf(MaxCountValidator(2)),
"border-image-slice" to listOf(MaxCountValidator(4)),
@@ -229,8 +233,49 @@ open class Style : CssGenerator() {
override fun getValidator(name: String) = validators[name]
private fun addStyle(selector: String, style: Css) {
definitions[selector] = definitions[selector] ?: mutableListOf()
definitions[selector]?.add(style)
}
fun select(selector: String, style: Css) {
definitions[selector] = style
addStyle(selector, style)
}
fun descendant(descName: String, style: Css) {
addStyle(descName, style)
}
fun cls(className: String, style: Css) {
addStyle(".$className", style)
}
fun child(childName: String, style: Css) {
addStyle("> $childName", style)
}
fun active(style: Css) {
addStyle(":active", style)
}
fun focus(style: Css) {
addStyle(":focus", style)
}
fun focusWithin(style: Css) {
addStyle(":focus-within", style)
}
fun hover(style: Css) {
addStyle(":hover", style)
}
fun visited(style: Css) {
addStyle(":visited", style)
}
fun plain(name: String, value: String) {
props[name] = prp(value)
}
fun alignContent(value: AlignContent) { props["align-content"] = prp(value) }
@@ -390,7 +435,7 @@ open class Style : CssGenerator() {
fun gridAutoFlow(flow: GridFlow) { props["grid-auto-flow"] = prp(flow) }
fun gridAutoRows(autoRows: GridAuto) { props["grid-auto-rows"] = prp(autoRows) }
fun gridAutoRows(size: Measurement) { props["grid-auto-rows"] = prp(size) }
fun gridColumn(start: GridRowColumn, end: GridRowColumn) { props["grid-column"] = prp(start, end) }
fun gridColumn(start: GridRowColumn, end: GridRowColumn) { props["grid-column"] = prp(CssProperty("${start.css()}/${end.css()}")) }
fun gridColumnEnd(end: GridRowColumn) { props["grid-column-end"] = prp(end) }
fun gridColumnGap(gap: GridRowColumn) { props["grid-column-gap"] = prp(gap) }
fun gridColumnStart(start: GridRowColumn) { props["grid-column-start"] = prp(start) }
@@ -398,13 +443,18 @@ open class Style : CssGenerator() {
rowGap: Measurement = Measurement.px(0),
columnGap: Measurement = Measurement.px(0)
) { props["grid-gap"] = prp(rowGap, columnGap) }
fun gridRow(start: GridRowColumn, end: GridRowColumn) { props["grid-row"] = prp(start, end) }
fun gridRow(start: GridRowColumn, end: GridRowColumn) { props["grid-row"] = prp(CssProperty("${start.css()}/${end.css()}")) }
fun gridRowEnd(end: GridRowColumn) { props["grid-row-end"] = prp(end) }
fun gridRowGap(gap: GridRowColumn) { props["grid-row-end"] = prp(gap) }
fun gridRowStart(start: GridRowColumn) { props["grid-row-start"] = prp(start) }
fun gridTemplate(template: String) { props["grid-template"] = prp(template) }
fun gridTemplateAreas(template: String) { props["grid-template-areas"] = prp(template) }
@Deprecated(
"Fixed type, use gridTemplateColumns instead",
ReplaceWith("gridTemplateColumns(columns)")
)
fun gridTemplateColumn(vararg columns: TemplateRowColumn) { props["grid-template-columns"] = prp(*columns) }
fun gridTemplateColumns(vararg columns: TemplateRowColumn) { props["grid-template-columns"] = prp(*columns) }
fun gridTemplateRows(vararg rows: TemplateRowColumn) { props["grid-template-rows"] = prp(*rows) }
fun hangingPunctuation(punctuation: Isolation) { props["hanging-punctuation"] = prp(punctuation) }
fun height(height: Measurement) { props["height"] = prp(height) }
@@ -462,6 +512,12 @@ open class Style : CssGenerator() {
fun overflowX(overflow: Overflow) { props["overflow-x"] = prp(overflow) }
fun overflowY(overflow: Overflow) { props["overflow-y"] = prp(overflow) }
fun padding(padding: Measurement) { props["padding"] = prp(padding) }
fun padding(topLeft: Measurement, bottomRight: Measurement) {
props["padding"] = prp(topLeft, bottomRight)
}
fun padding(top: Measurement, right: Measurement, bottom: Measurement, left: Measurement) {
props["padding"] = prp(top, right, bottom, left)
}
fun padding(padding: InitialInherit) { props["padding"] = prp(padding) }
fun paddingBottom(padding: Measurement) { props["padding-bottom"] = prp(padding) }
fun paddingBottom(padding: InitialInherit) { props["padding-bottom"] = prp(padding) }

View File

@@ -1,5 +0,0 @@
package nl.astraeus.logging
import mu.KotlinLogging
val log = KotlinLogging.logger {}