Took 14 seconds
This commit is contained in:
2021-08-30 12:38:52 +02:00
parent d4a8b18ec2
commit ca68871eca
6 changed files with 237 additions and 55 deletions

View File

@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform") version "1.5.21"
kotlin("multiplatform") version "1.5.30"
`maven-publish`
}
@@ -7,9 +7,9 @@ group = "nl.astraeus"
version = "0.4.29-SNAPSHOT"
repositories {
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
mavenLocal()
mavenCentral()
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
}
kotlin {

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -10,4 +10,4 @@ pluginManagement {
rootProject.name = "kotlin-css-generator"
enableFeaturePreview("GRADLE_METADATA")
//enableFeaturePreview("GRADLE_METADATA")

View File

@@ -4,6 +4,7 @@ enum class MeasurementUoM {
NONE,
PX,
EM,
REL,
REM,
PC,
PRC,
@@ -16,12 +17,23 @@ open class Measurement(
val uom: MeasurementUoM = MeasurementUoM.NONE
) : CssProperty(value) {
override fun toString(): String = super.value
companion object {
val auto = Measurement("auto")
val initial = Measurement("initial")
val inherit = Measurement("inherit")
val normal = Measurement("normal")
fun fromString(value:String): Measurement = when {
value == "0" -> Measurement("0", MeasurementUoM.PX)
value.endsWith("px") -> Measurement(value.slice(0..(value.length-2)), MeasurementUoM.PX)
value.endsWith("rel") -> Measurement(value.slice(0..(value.length-3)), MeasurementUoM.REL)
else -> {
TODO("Unable to parse $value")
}
}
fun px(nr: Int) = if (nr == 0) {
Measurement(
"0",

View File

@@ -61,32 +61,140 @@ abstract class CssGenerator {
return "$paddedName$builder;\n"
}
fun generatePropertyCss(indent: String): String {
fun generatePropertyCss(
indent: String,
sortProperties: Boolean
): String {
val builder = StringBuilder()
for ((name, prop) in props) {
builder.append(propertyCss(indent, name, prop))
if (sortProperties) {
for (name in props.keys.sorted()) {
val prop = props[name] ?: error("$name not found in properties after sorting!")
builder.append(propertyCss(indent, name, prop))
}
} else {
for ((name, prop) in props) {
builder.append(propertyCss(indent, name, prop))
}
}
return builder.toString()
}
open fun generateCss(
indent: String = "",
minified: Boolean = false,
warnOnRedeclaration: Boolean = true,
allowCommaInSelector: Boolean = false,
combineEqualBlocks: Boolean = false,
sortProperties: Boolean = false
): String {
val blocks = generateCssBlocks(
indent = indent,
minified = minified,
warnOnRedeclaration = warnOnRedeclaration,
allowCommaInSelector = allowCommaInSelector,
sortProperties = sortProperties
)
val builder = StringBuilder()
fun StringBuilder.generateBlock(
indent: String,
selectors: List<String>,
block: CssBlock?
) {
if (selectors.isNotEmpty() && block != null) {
append(indent)
append(selectors.joinToString(",\n"))
append(" {\n")
append(block.content)
append(indent)
append("}\n\n")
}
}
if (!combineEqualBlocks) {
var first = true
val selectors = mutableListOf<String>()
var lastBlock: CssBlock? = null
for (block in blocks) {
if (first) {
first = false
selectors.add(block.selector)
lastBlock = block
} else {
lastBlock = if (lastBlock != null && lastBlock.content == block.content) {
selectors.add(block.selector)
block
} else {
builder.generateBlock(indent, selectors, lastBlock)
selectors.clear()
selectors.add(block.selector)
block
}
}
}
builder.generateBlock(indent, selectors, lastBlock)
} else {
val blockHashes: MutableMap<Int, MutableList<CssBlock>> = mutableMapOf()
for (block in blocks) {
blockHashes.getOrPut(block.content.hashCode()) {
mutableListOf()
}.add(block)
}
val done = mutableSetOf<Int>()
for(block in blocks) {
val hashCode = block.content.hashCode()
if (!done.contains(hashCode)) {
blockHashes[hashCode]?.let {
val slctrs = it.map { blk ->
blk.selector
}
builder.generateBlock(indent, slctrs, block)
done.add(hashCode)
}
}
}
}
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()
}
}
open fun generateCssBlocks(
namespace: String = "",
indent: String = "",
minified: Boolean = false,
warnOnRedeclaration: Boolean = true,
allowCommaInSelector: Boolean = false
): String {
allowCommaInSelector: Boolean = false,
sortProperties: Boolean = false
): List<CssBlock> {
val blocks = mutableListOf<CssBlock>()
val builder = StringBuilder()
for (name in definitions.keys) {
val props = definitions[name]!!
val css = StringBuilder()
if (warnOnRedeclaration && props.size > 1) {
css.append("/* style '$name' is defined ${props.size} times! */\n")
css.append(" $indent/* style '$name' is defined ${props.size} times! */\n")
}
val finalStyle = Style()
@@ -95,22 +203,24 @@ abstract class CssGenerator {
prop(finalStyle)
}
css.append(finalStyle.generatePropertyCss(" $indent"))
css.append(finalStyle.generatePropertyCss(" $indent", sortProperties))
if (css.isNotBlank()) {
val builder = StringBuilder()
check (allowCommaInSelector || !name.contains(',')) {
"Comma is not allowed in selector (option is set in generateCss call)"
}
builder.append("\n$namespace$name".trim())
//builder.append("\n$namespace$name".trim())
//builder.append(" $indent")
builder.append(" {\n")
//builder.append(" {\n")
finalStyle.fontFace?.let { ff ->
builder.append(" $indent")
builder.append("@font-face {\n")
builder.append(ff.generatePropertyCss(" $indent"))
builder.append(ff.generatePropertyCss(" $indent", sortProperties))
builder.append(" $indent")
builder.append("}\n")
}
@@ -134,7 +244,7 @@ abstract class CssGenerator {
style(finalStyle)
builder.append(finalStyle.generatePropertyCss(" $indent"))
builder.append(finalStyle.generatePropertyCss(" $indent", sortProperties))
builder.append(" $indent")
builder.append("}\n")
@@ -147,13 +257,21 @@ abstract class CssGenerator {
}
builder.append(css)
builder.append("}\n\n")
//builder.append("}\n\n")
blocks.add(CssBlock(
"$namespace$name".trim(),
builder.toString()
))
}
builder.append(finalStyle.generateCss(
blocks.addAll(finalStyle.generateCssBlocks(
"$namespace$name".trim(),
indent,
allowCommaInSelector = allowCommaInSelector
minified = minified,
allowCommaInSelector = allowCommaInSelector,
warnOnRedeclaration = warnOnRedeclaration,
sortProperties = sortProperties
))
}
@@ -162,24 +280,22 @@ abstract class CssGenerator {
mq.keys.sorted().forEach { mediaName ->
val css = mq[mediaName]
builder.append(indent)
builder.append("@media ")
builder.append(mediaName)
builder.append(" {\n")
css?.let { css ->
val mediaStyle = ConditionalStyle()
css(mediaStyle)
builder.append(mediaStyle.generateCss(
"",
" $indent",
allowCommaInSelector = allowCommaInSelector
blocks.add(CssBlock(
"$indent@media $mediaName".trim(),
mediaStyle.generateCss(
" $indent",
minified = minified,
allowCommaInSelector = allowCommaInSelector,
warnOnRedeclaration = warnOnRedeclaration,
sortProperties = sortProperties
)
))
}
builder.append(indent)
builder.append("}\n")
}
}
@@ -187,40 +303,27 @@ abstract class CssGenerator {
mq.keys.sorted().forEach { mediaName ->
val css = mq[mediaName]
builder.append(indent)
builder.append("@supports ")
builder.append(mediaName)
builder.append(" {\n")
css?.let { css ->
val mediaStyle = ConditionalStyle()
css(mediaStyle)
builder.append(mediaStyle.generateCss(
"",
" $indent",
allowCommaInSelector = allowCommaInSelector
blocks.add(CssBlock(
"$indent@supports $mediaName".trim(),
mediaStyle.generateCss(
" $indent",
minified = minified,
allowCommaInSelector = allowCommaInSelector,
warnOnRedeclaration = warnOnRedeclaration,
sortProperties = sortProperties
)
))
}
builder.append(indent)
builder.append("}\n")
}
}
}
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()
}
return blocks
}
}
@@ -382,7 +485,19 @@ open class Style : CssGenerator() {
addStyle(":hover", style)
}
fun pseudo(selector: DescriptionProvider, style: Css) {
fun firstChild(style: Css) {
addStyle(":first-child", style)
}
fun lastChild(style: Css) {
addStyle(":last-child", style)
}
fun pseudoElement(selector: DescriptionProvider, style: Css) {
addStyle(":${selector.description()}", style)
}
fun pseudoChild(selector: DescriptionProvider, style: Css) {
addStyle("::${selector.description()}", style)
}
@@ -486,6 +601,10 @@ open class Style : CssGenerator() {
props["background-image"] = prp(image)
}
fun backgroundImage(value: String) {
props["background-image"] = prp(value)
}
fun backgroundOrigin(origin: ClipOrigin) {
props["background-origin"] = prp(origin)
}
@@ -506,6 +625,14 @@ open class Style : CssGenerator() {
props["border"] = prp(border)
}
fun border(
width: Measurement,
style: BorderStyle,
color: Color
) {
props["border"] = prp(width, style, color)
}
fun borderBottom(borderBottom: String) {
props["border-bottom"] = prp(borderBottom)
}
@@ -514,10 +641,18 @@ open class Style : CssGenerator() {
props["border-bottom-color"] = prp(color)
}
fun borderBottomLeftRadius(vararg radius: Measurement) {
props["border-bottom-left-radius"] = prp(*radius)
}
fun borderBottomLeftRadius(vararg radius: BorderRadius) {
props["border-bottom-left-radius"] = prp(*radius)
}
fun borderBottomRightRadius(vararg radius: Measurement) {
props["border-bottom-right-radius"] = prp(*radius)
}
fun borderBottomRightRadius(vararg radius: BorderRadius) {
props["border-bottom-right-radius"] = prp(*radius)
}
@@ -656,10 +791,18 @@ open class Style : CssGenerator() {
props["border-top-color"] = prp(color)
}
fun borderTopLeftRadius(radius: Measurement) {
props["border-top-left-radius"] = prp(radius)
}
fun borderTopLeftRadius(radius: BorderRadius) {
props["border-top-left-radius"] = prp(radius)
}
fun borderTopRightRadius(radius: Measurement) {
props["border-top-right-radius"] = prp(radius)
}
fun borderTopRightRadius(radius: BorderRadius) {
props["border-top-right-radius"] = prp(radius)
}

View File

@@ -1,5 +1,6 @@
package nl.astraeus.css
import nl.astraeus.css.properties.BoxSizing
import nl.astraeus.css.properties.Color
import nl.astraeus.css.properties.Count
import nl.astraeus.css.properties.Display
@@ -24,6 +25,32 @@ class TestCssBuilder {
fun testBuilder() {
val css = style {
select("*", "*::before", "*::after") {
boxSizing(BoxSizing.borderBox)
}
select("html") {
transition("background-color 1s ease")
margin(0.px)
padding(0.px)
focus {
backgroundColor(Color.blue)
}
}
select("body") {
margin(0.px)
padding(0.px)
focus {
backgroundColor(Color.blue)
}
transition("background-color 1s ease")
}
select(".test") {
top(10.px)
left(4.em)
@@ -77,7 +104,7 @@ class TestCssBuilder {
}
}
println(css.generateCss())
println(css.generateCss(combineEqualBlocks = true, sortProperties = true))
}
@Test