Fixes
Took 14 seconds
This commit is contained in:
@@ -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 {
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
||||
@@ -10,4 +10,4 @@ pluginManagement {
|
||||
|
||||
rootProject.name = "kotlin-css-generator"
|
||||
|
||||
enableFeaturePreview("GRADLE_METADATA")
|
||||
//enableFeaturePreview("GRADLE_METADATA")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user