Add option to overwrite and redeclaration warning option
This commit is contained in:
@@ -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
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
src/commonMain/kotlin/nl/astraeus/css/style/CssFunctions.kt
Normal file
11
src/commonMain/kotlin/nl/astraeus/css/style/CssFunctions.kt
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) }
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package nl.astraeus.logging
|
||||
|
||||
import mu.KotlinLogging
|
||||
|
||||
val log = KotlinLogging.logger {}
|
||||
Reference in New Issue
Block a user