diff --git a/.gitignore b/.gitignore index 2bff224..cbd0275 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build .idea local.properties +gradle.properties diff --git a/build.gradle.kts b/build.gradle.kts index 0048a97..628aeeb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,12 +1,10 @@ -import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport - plugins { - kotlin("multiplatform") version "1.4.0-rc" + kotlin("multiplatform") version "1.4.30" `maven-publish` } group = "nl.astraeus" -version = "0.3.6-SNAPSHOT" +version = "0.3.10-SNAPSHOT" repositories { maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") } @@ -17,21 +15,36 @@ repositories { kotlin { jvm() js(BOTH) { - browser() - - //produceKotlinLibrary() - } - val hostOs = System.getProperty("os.name") - val isMingwX64 = hostOs.startsWith("Windows") - val nativeTarget = when { - hostOs == "Mac OS X" -> macosX64("native") - hostOs == "Linux" -> linuxX64("native") - isMingwX64 -> mingwX64("native") - else -> throw GradleException("Host OS is not supported in Kotlin/Native.") + browser { + testTask { + useKarma { + useFirefox() + //useChrome() + } + } + } } sourceSets { - val commonMain by getting {} + val commonMain by getting { + + } + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + val jvmTest by getting { + dependencies { + implementation(kotlin("test-junit")) + } + } + val jsTest by getting { + dependencies { + implementation(kotlin("test-js")) + } + } } } @@ -68,3 +81,4 @@ publishing { } } } + diff --git a/gradle.properties b/gradle.properties index 6c04925..eb94736 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,5 @@ kotlin.code.style=official kotlin.js.compiler=both -nexusUsername= -nexusPassword= - +nexusUsername=deployment +nexusPassword=nhPTzBWizph86j2Jz5KhPdiB4C diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/Color.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/Color.kt index a08a5ce..ab058a3 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/Color.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/Color.kt @@ -1,9 +1,13 @@ package nl.astraeus.css.properties -class Color( - value: String, - val readOnly: Boolean = true +import kotlin.math.max +import kotlin.math.min + +open class Color( + value: String, + val readOnly: Boolean = false ) : CssProperty(value) { + /* val hue: Int? = null val saturation: Int? = null val lightness: Int? = null @@ -41,42 +45,46 @@ class Color( return this }*/ - companion object { - val auto = Color("auto") - val transparant = Color("transparant") - val initial = Color("initial") - val inherit = Color("inherit") - fun hex(hex: String) = Color("#$hex") - fun rgb( - red: Int, - green: Int, - blue: Int - ) = Color("rgb($red, $green, $blue)") - fun rgba( - red: Int, - green: Int, - blue: Int, - alpha: Double - ) = Color("rgba($red, $green, $blue, $alpha)") - fun hsl( - hue: Int, - saturation: Int, - lightness: Int - ) = Color("hsl($hue, $saturation%, $lightness%)") - fun hsla( - hue: Int, - saturation: Int, - lightness: Int, - alpha: Double - ) = Color("hsla($hue, $saturation%, $lightness%, $alpha)") + companion object { + val auto = Color("auto", true) + val transparant = Color("transparant", true) + val initial = Color("initial", true) + val inherit = Color("inherit", true) - private fun fromHex( - red: Int, - green: Int, - blue: Int - ) { + fun hex(hex: String) = Color("#$hex") + fun rgb( + red: Int, + green: Int, + blue: Int + ) = Color("rgb($red, $green, $blue)") + + fun rgba( + red: Int, + green: Int, + blue: Int, + alpha: Double + ) = Color("rgba($red, $green, $blue, $alpha)") + + fun hsl( + hue: Int, + saturation: Int, + lightness: Int + ) = Color("hsl($hue, $saturation%, $lightness%)") + + fun hsla( + hue: Int, + saturation: Int, + lightness: Int, + alpha: Double + ) = Color("hsla($hue, $saturation%, $lightness%, $alpha)") + + private fun fromHex( + red: Int, + green: Int, + blue: Int + ) { - } } + } } diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt index 4301e7d..eed8318 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/CssProperty.kt @@ -5,7 +5,7 @@ interface CssValue { } open class CssProperty( - val value: String + var value: String ): CssValue { override fun css(): String = value diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/HslaColor.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/HslaColor.kt new file mode 100644 index 0000000..4bb92d0 --- /dev/null +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/HslaColor.kt @@ -0,0 +1,168 @@ +package nl.astraeus.css.properties + +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.min + +class HslaColor( + val hue: Int, + val saturation: Int, + val lightness: Int, + val alpha: Double = 1.0 +): Color("hsla($hue,$saturation%,$lightness%,$alpha)") { + + fun darken(amount: Int): HslaColor = HslaColor( + hue, + saturation, + max(0, lightness - amount), + alpha + ) + + fun lighten(amount: Int): HslaColor = HslaColor( + hue, + saturation, + min(100, lightness + amount), + alpha + ) + + fun saturize(amount: Int): HslaColor = HslaColor( + hue, + min(100, saturation + amount), + lightness, + alpha + ) + + fun desaturize(amount: Int): HslaColor = HslaColor( + hue, + max(0, saturation - amount), + lightness, + alpha + ) + + fun adjustHue(amount: Int): HslaColor { + var hue = this.hue + amount + while (hue < 0) { + hue += 360 + } + while(hue > 359) { + hue -= 360 + } + + return HslaColor( + hue, + saturation, + lightness, + alpha + ) + } + + fun toRgba() = RgbaColor.fromHsla(this) + + companion object { + private fun findValues(value: String, expectedNumber: Int): List { + check(value.contains('(') && value.contains(')')) + + val parts = value.split('(')[1].split(')')[0].split(',') + + check(parts.size == expectedNumber) + + return parts + } + + fun fromString(value: String): HslaColor = when { + value.trim().startsWith("#") -> { + fromHex(value.substringAfterLast('#')) + } + value.trim().startsWith("hsla") -> { + val values = findValues(value, 4) + HslaColor( + values[0].trim().toInt(), + values[1].trim().toInt(), + values[2].trim().toInt(), + values[3].trim().toDouble() + ) + } + value.trim().startsWith("hsl") -> { + val values = findValues(value, 3) + HslaColor( + values[0].trim().toInt(), + values[1].trim().toInt(), + values[2].trim().toInt() + ) + } + value.trim().startsWith("rgba") -> { + val values = findValues(value, 4) + fromRgba( + values[0].trim().toInt(), + values[1].trim().toInt(), + values[2].trim().toInt(), + values[3].trim().toDouble() + ) + } + value.trim().startsWith("rgb") -> { + val values = findValues(value, 3) + fromRgb( + values[0].trim().toInt(), + values[1].trim().toInt(), + values[2].trim().toInt() + ) + } + else -> { + throw IllegalArgumentException("Unable to parse $value as color") + } + } + + fun fromHex(value: String): HslaColor = fromRgba(RgbaColor.fromHex(value)) + + fun fromRgba(rgba: RgbaColor): HslaColor = fromRgba(rgba.red, rgba.green, rgba.blue, rgba.alpha) + + fun fromRgb( + red: Int, + green: Int, + blue: Int, + ): HslaColor { + return fromRgba(red, green, blue, 1.0) + } + + fun fromRgba( + red: Int, + green: Int, + blue: Int, + alpha: Double + ): HslaColor { + val r = red / 255.0 + val g = green / 255.0 + val b = blue / 255.0 + + val v = max(r,max(g,b)) + val c = v - min(r,min(g,b)) + val f = (1 - abs(v+v-c-1)) + var h = if (c > 0) { + when (v) { + r -> { + (g-b)/c + } + g -> { + 2+(b-r)/c + } + else -> { + 2+(b-r)/c + } + } + } else { + 0.0 + } + + if (h < 0) { + h += 6 + } + + return HslaColor( + (60*h).toInt(), + if (f>0) { ((c * 100)/f).toInt() } else { 0 }, + (100*((v+v-c)/2)).toInt(), + alpha + ) + } + } +} diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/Measurement.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/Measurement.kt index 01f49c7..8cbc1a0 100644 --- a/src/commonMain/kotlin/nl/astraeus/css/properties/Measurement.kt +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/Measurement.kt @@ -11,28 +11,41 @@ open class Measurement( val normal = Measurement("normal") fun px(nr: Int) = if (nr == 0) { Measurement("0") } else { Measurement("${nr}px") } - fun px(nr: Double) = Measurement("${nr}px") - fun em(nr: Int) = Measurement("${nr}em") - fun em(nr: Double) = Measurement("${nr}em") - fun perc(nr: Int) = Measurement("${nr}%") - fun perc(nr: Double) = Measurement("${nr}%") - fun pc(nr: Int) = Measurement("${nr}pc") - fun pc(nr: Double) = Measurement("${nr}pc") - fun cm(nr: Int) = Measurement("${nr}cm") - fun cm(nr: Double) = Measurement("${nr}cm") + fun px(nr: Double) = nr.px + fun em(nr: Int) = nr.em + fun em(nr: Double) = nr.em + fun perc(nr: Int) = nr.perc + fun perc(nr: Double) = nr.perc + fun pc(nr: Int) = nr.pc + fun pc(nr: Double) = nr.pc + fun cm(nr: Int) = nr.cm + fun cm(nr: Double) = nr.cm } } +val Int.px: Measurement + get() = Measurement("${this}${if (this == 0) { "" } else { "px"}}") +val Int.em: Measurement + get() = Measurement("${this}${if (this == 0) { "" } else { "em"}}") +val Int.perc: Measurement + get() = Measurement("${this}%") +val Int.pc: Measurement + get() = Measurement("${this}pc") +val Int.cm: Measurement + get() = Measurement("${this}cm") fun Int.px(): Measurement = Measurement.px(this) + +val Double.px: Measurement + get() = Measurement("${this}px") +val Double.em: Measurement + get() = Measurement("${this}em") +val Double.perc: Measurement + get() = Measurement("${this}%") +val Double.pc: Measurement + get() = Measurement("${this}pc") +val Double.cm: Measurement + get() = Measurement("${this}cm") fun Double.px(): Measurement = Measurement.px(this) -fun Int.em(): Measurement = Measurement.em(this) -fun Double.em(): Measurement = Measurement.em(this) -fun Int.perc(): Measurement = Measurement.perc(this) -fun Double.perc(): Measurement = Measurement.perc(this) -fun Int.pc(): Measurement = Measurement.pc(this) -fun Double.pc(): Measurement = Measurement.pc(this) -fun Int.cm(): Measurement = Measurement.cm(this) -fun Double.cm(): Measurement = Measurement.cm(this) open class LineHeight(value: String) : CssProperty(value) { companion object { diff --git a/src/commonMain/kotlin/nl/astraeus/css/properties/RgbaColor.kt b/src/commonMain/kotlin/nl/astraeus/css/properties/RgbaColor.kt new file mode 100644 index 0000000..a5fb42c --- /dev/null +++ b/src/commonMain/kotlin/nl/astraeus/css/properties/RgbaColor.kt @@ -0,0 +1,107 @@ +package nl.astraeus.css.properties + +import kotlin.math.max +import kotlin.math.min + +class RgbaColor( + val red: Int, + val green: Int, + val blue: Int, + val alpha: Double = 1.0 +): Color("rgba($red,$green,$blue,$alpha)") { + + fun toHsla(): HslaColor = HslaColor.fromRgba(this) + + companion object { + private fun findValues(value: String, expectedNumber: Int): List { + check(value.contains('(') && value.contains(')')) + + val parts = value.split('(')[1].split(')')[0].split(',') + + check(parts.size == expectedNumber) + + return parts + } + + fun fromString(value: String): RgbaColor = when { + value.trim().startsWith("#") -> { + fromHex(value.substringAfterLast('#')) + } + value.trim().startsWith("hsla") -> { + val values = findValues(value, 4) + fromHsla( + values[0].toInt(), + values[1].toInt(), + values[2].toInt(), + values[3].toDouble() + ) + } + value.trim().startsWith("hsl") -> { + val values = findValues(value, 3) + fromHsl( + values[0].toInt(), + values[1].toInt(), + values[2].toInt() + ) + } + value.trim().startsWith("rgba") -> { + val values = findValues(value, 4) + RgbaColor( + values[0].toInt(), + values[1].toInt(), + values[2].toInt(), + values[3].toDouble() + ) + } + value.trim().startsWith("rgb") -> { + val values = findValues(value, 3) + RgbaColor( + values[0].toInt(), + values[1].toInt(), + values[2].toInt() + ) + } + else -> { + throw IllegalArgumentException("Unable to parse $value as color") + } + } + + fun fromHex(value: String): RgbaColor { + TODO("Implement parsing hex to rgba") + } + + fun fromHsla(hsla: HslaColor): RgbaColor = fromHsla( + hsla.hue, + hsla.saturation, + hsla.lightness, + hsla.alpha + ) + + fun fromHsl( + hue: Int, + saturation: Int, + lightness: Int + ): RgbaColor { + return fromHsla(hue, saturation, lightness, 1.0) + } + + fun fromHsla( + hue: Int, + saturation: Int, + lightness: Int, + alpha: Double + ): RgbaColor { + val h = hue + val s = saturation / 100.0 + val l = lightness / 100.0 + + val a = s * min(l, 1 - l) + fun f(n: Int): Int { + val k: Int = (n + h / 30) % 12 + return (255 * (l - a * max(min(k - 3, min(9 - k, 1)), -1))).toInt() + } + + return RgbaColor(f(0), f(8), f(4), alpha) + } + } +} diff --git a/src/commonTest/kotlin/nl/astraeus/css/TestColor.kt b/src/commonTest/kotlin/nl/astraeus/css/TestColor.kt new file mode 100644 index 0000000..167edb1 --- /dev/null +++ b/src/commonTest/kotlin/nl/astraeus/css/TestColor.kt @@ -0,0 +1,71 @@ +package nl.astraeus.css + +import nl.astraeus.css.properties.* +import kotlin.test.Test + +class TestColor { + + @Test + fun testBuilder() { + val css = style { + val baseColor = HslaColor(20, 50, 50) + val baseColorRgb = RgbaColor.fromHsla(baseColor) + + println("BaseColor: ${baseColor.value} - ${baseColorRgb.value}") + + val darkerColor = baseColor.darken(10) + val darkerColorRgb = RgbaColor.fromHsla(darkerColor) + println("DarkerColor: ${darkerColor.value} - ${darkerColorRgb.value}") + + val saturizedColor = baseColor.saturize(10) + val saturizedColorRgb = RgbaColor.fromHsla(saturizedColor) + println("SaturizedColor: ${saturizedColor.value} - ${saturizedColorRgb.value}") + + val redColor = RgbaColor(0,255,0,1.0) + val redColorHsla = redColor.toHsla() + val shiftedColor = redColorHsla.adjustHue(120) + val shiftedColorRgb = shiftedColor.toRgba() + + println("RedColor: ${redColor.value} - ${redColorHsla.value}") + println("ShiftedColor: ${shiftedColor.value} - ${shiftedColorRgb.value}") + + select(".test") { + top(10.px) + left(4.em) + + backgroundColor( + RgbaColor.fromHsla(baseColor.darken(10)) + ) + + animationIterationMode( + Count.auto, + Count.auto, + Count.auto, + Count.auto, + Count.auto + ) + + child("li") { + color(Color.hsl(200, 50, 50)) + } + + select("> a") { + color(Color.hsl(200, 50, 50)) + } + } + } + + println(css.generateCss()) + + val css2 = style { + cls("button") { + fontSize(12.px) + color(Color.hsl(200, 50, 50)) + } + } + + println(css2.generateCss()) + + } + +} \ No newline at end of file diff --git a/src/commonTest/kotlin/nl/astraeus/css/TestCssBuilder.kt b/src/commonTest/kotlin/nl/astraeus/css/TestCssBuilder.kt index 3aa9367..cabeb53 100644 --- a/src/commonTest/kotlin/nl/astraeus/css/TestCssBuilder.kt +++ b/src/commonTest/kotlin/nl/astraeus/css/TestCssBuilder.kt @@ -1,16 +1,20 @@ package nl.astraeus.css -object TestCssBuilder { -/* - @Test - fun testBuilder() { - val css = CssBuilder() +import nl.astraeus.css.properties.Color.Companion.hsl +import nl.astraeus.css.properties.Color.Companion.rgba +import nl.astraeus.css.properties.Count +import nl.astraeus.css.properties.em +import nl.astraeus.css.properties.px - css.style { +object TestCssBuilder { + + //@Test + fun testBuilder() { + val css = css { select(".test") { - top(10.px()) - left(4.em()) + top(10.px) + left(4.em) backgroundColor(rgba(255, 255, 255, 0.75)) animationIterationMode( Count.auto, @@ -20,6 +24,9 @@ object TestCssBuilder { Count.auto ) + child("li") { + color(hsl(200,50,50)) + } select("> a") { color(hsl(200, 50, 50)) @@ -28,6 +35,17 @@ object TestCssBuilder { } println(css) - }*/ + + val css2 = css { + cls("button") { + fontSize(12.px) + color(hsl(200, 50, 50)) + } + } + + println(css2) + + + } }