Browse Source

Feature/calc (#790)

* CSS operations

* CSS operations with variables

* Fix api to use CSSColorValue instead of Color to use CSSVariables too

* Fix api to use CSSNumericValue instead of CSSSizeValue to use CSSVariables too

* Fix CSSBorder types
pull/798/head
Abasov Akif 3 years ago committed by GitHub
parent
commit
925f697a6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      web/core/src/jsMain/kotlin/androidx/compose/web/css/CSSOperations.kt
  2. 58
      web/core/src/jsMain/kotlin/androidx/compose/web/css/CSSProperties.kt
  3. 3
      web/core/src/jsMain/kotlin/androidx/compose/web/css/CSSUnits.kt
  4. 4
      web/core/src/jsMain/kotlin/androidx/compose/web/css/Color.kt
  5. 24
      web/core/src/jsMain/kotlin/androidx/compose/web/css/StyleBuilder.kt
  6. 191
      web/core/src/jsTest/kotlin/CSSUnitApiTests.kt

52
web/core/src/jsMain/kotlin/androidx/compose/web/css/CSSOperations.kt

@ -11,4 +11,54 @@ operator fun <T: CSSUnit> Number.times(unit: CSSSizeValue<T>): CSSSizeValue<T> =
operator fun <T: CSSUnit> CSSSizeValue<T>.div(num: Number): CSSSizeValue<T> = CSSUnitValueTyped(value / num.toFloat(), unit)
operator fun <T: CSSUnit> CSSSizeValue<T>.plus(b: CSSSizeValue<T>): CSSSizeValue<T> = CSSUnitValueTyped(value + b.value, unit)
operator fun <T: CSSUnit> CSSSizeValue<T>.minus(b: CSSSizeValue<T>): CSSSizeValue<T> = CSSUnitValueTyped(value - b.value, unit)
operator fun <T: CSSUnit> CSSSizeValue<T>.minus(b: CSSSizeValue<T>): CSSSizeValue<T> = CSSUnitValueTyped(value - b.value, unit)
external interface CSSCalcOperation<T : CSSUnit>: CSSNumericValue<T>
data class CSSCalcValue<T : CSSUnit>(
var op: CSSCalcOperation<out T>
) : CSSCalcOperation<T> {
override fun toString(): String = "calc$op"
}
data class CSSPlus<T : CSSUnit>(
var l: CSSNumericValue<out T>,
var r: CSSNumericValue<out T>
) : CSSCalcOperation<T> {
override fun toString(): String = "($l + $r)"
}
data class CSSMinus<T : CSSUnit>(
var l: CSSNumericValue<out T>,
var r: CSSNumericValue<out T>
) : CSSCalcOperation<T> {
override fun toString(): String = "($l - $r)"
}
data class CSSTimes<T : CSSUnit>(
var l: CSSNumericValue<out T>,
var r: Number,
val left: Boolean = true
) : CSSCalcOperation<T> {
override fun toString(): String = if (left) "($l * $r)" else "($r * $l)"
}
data class CSSDiv<T : CSSUnit>(
var l: CSSNumericValue<out T>,
var r: Number
) : CSSCalcOperation<T> {
override fun toString(): String = "($l / $r)"
}
operator fun <T: CSSUnit> CSSNumericValue<out T>.plus(b: CSSNumericValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSPlus(this, b))
operator fun <T: CSSUnit> CSSCalcValue<out T>.plus(b: CSSNumericValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSPlus(this.op, b))
operator fun <T: CSSUnit> CSSNumericValue<out T>.plus(b: CSSCalcValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSPlus(this, b.op))
operator fun <T: CSSUnit> CSSNumericValue<out T>.minus(b: CSSNumericValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSMinus(this, b))
operator fun <T: CSSUnit> CSSCalcValue<out T>.minus(b: CSSNumericValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSMinus(this.op, b))
operator fun <T: CSSUnit> CSSNumericValue<out T>.minus(b: CSSCalcValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSMinus(this, b.op))
operator fun <T: CSSUnit> CSSCalcValue<out T>.times(b: Number): CSSCalcValue<T> = CSSCalcValue(CSSTimes(this.op, b))
operator fun <T: CSSUnit> Number.times(b: CSSCalcValue<out T>): CSSCalcValue<T> = CSSCalcValue(CSSTimes(b.op, this, false))
operator fun <T: CSSUnit> CSSCalcValue<out T>.div(b: Number): CSSCalcValue<T> = CSSCalcValue(CSSDiv(this.op, b))

58
web/core/src/jsMain/kotlin/androidx/compose/web/css/CSSProperties.kt

@ -28,16 +28,12 @@ fun StyleBuilder.color(value: String) {
property("color", value)
}
fun StyleBuilder.color(value: Color) {
fun StyleBuilder.color(value: CSSColorValue) {
// color hasn't Typed OM yet
property("color", value)
}
fun StyleBuilder.backgroundColor(value: CSSVariableValue<Color>) {
property("background-color", value)
}
fun StyleBuilder.backgroundColor(value: Color) {
fun StyleBuilder.backgroundColor(value: CSSColorValue) {
property("background-color", value)
}
@ -47,9 +43,9 @@ fun StyleBuilder.backgroundColor(value: String) {
@Suppress("EqualsOrHashCode")
class CSSBorder : CSSStyleValue {
var width: CSSUnitValue? = null
var style: StylePropertyValue? = null
var color: StylePropertyValue? = null
var width: CSSNumeric? = null
var style: LineStyle? = null
var color: CSSColorValue? = null
override fun equals(other: Any?): Boolean {
return if (other is CSSBorder) {
@ -63,19 +59,15 @@ class CSSBorder : CSSStyleValue {
}
}
inline fun CSSBorder.width(size: CSSUnitValue) {
inline fun CSSBorder.width(size: CSSNumeric) {
width = size
}
inline fun CSSBorder.style(style: LineStyle) {
this.style = CSSStyleValue(style.name)
}
inline fun CSSBorder.color(color: Color) {
this.color = color
this.style = style
}
inline fun CSSBorder.color(color: CSSVariableValue<Color>) {
inline fun CSSBorder.color(color: CSSColorValue) {
this.color = color
}
@ -86,7 +78,7 @@ inline fun StyleBuilder.border(crossinline borderBuild: CSSBorder.() -> Unit) {
fun StyleBuilder.border(
width: CSSLengthValue? = null,
style: LineStyle? = null,
color: Color? = null
color: CSSColorValue? = null
) {
border {
width?.let { width(it) }
@ -148,27 +140,27 @@ fun StyleBuilder.position(position: Position) {
)
}
fun StyleBuilder.borderRadius(r: CSSUnitValue) {
fun StyleBuilder.borderRadius(r: CSSNumeric) {
property("border-radius", r)
}
fun StyleBuilder.borderRadius(topLeft: CSSUnitValue, bottomRight: CSSUnitValue) {
fun StyleBuilder.borderRadius(topLeft: CSSNumeric, bottomRight: CSSNumeric) {
property("border-radius", "$topLeft $bottomRight")
}
fun StyleBuilder.borderRadius(
topLeft: CSSUnitValue,
topRightAndBottomLeft: CSSUnitValue,
bottomRight: CSSUnitValue
topLeft: CSSNumeric,
topRightAndBottomLeft: CSSNumeric,
bottomRight: CSSNumeric
) {
property("border-radius", "$topLeft $topRightAndBottomLeft $bottomRight")
}
fun StyleBuilder.borderRadius(
topLeft: CSSUnitValue,
topRight: CSSUnitValue,
bottomRight: CSSUnitValue,
bottomLeft: CSSUnitValue
topLeft: CSSNumeric,
topRight: CSSNumeric,
bottomRight: CSSNumeric,
bottomLeft: CSSNumeric
) {
property(
"border-radius",
@ -176,7 +168,7 @@ fun StyleBuilder.borderRadius(
)
}
fun StyleBuilder.width(value: CSSUnitValue) {
fun StyleBuilder.width(value: CSSNumeric) {
property("width", value)
}
@ -184,7 +176,7 @@ fun StyleBuilder.width(value: CSSAutoKeyword) {
property("width", value)
}
fun StyleBuilder.height(value: CSSUnitValue) {
fun StyleBuilder.height(value: CSSNumeric) {
property("height", value)
}
@ -224,24 +216,24 @@ fun StyleBuilder.right(value: CSSAutoKeyword) {
property("right", value)
}
fun StyleBuilder.fontSize(value: CSSUnitValue) {
fun StyleBuilder.fontSize(value: CSSNumeric) {
property("font-size", value)
}
fun StyleBuilder.margin(value: CSSUnitValue) {
fun StyleBuilder.margin(value: CSSNumeric) {
// marign hasn't Typed OM yet
property("margin", value)
}
fun StyleBuilder.marginLeft(value: CSSUnitValue) {
fun StyleBuilder.marginLeft(value: CSSNumeric) {
property("margin-left", value)
}
fun StyleBuilder.marginTop(value: CSSUnitValue) {
fun StyleBuilder.marginTop(value: CSSNumeric) {
property("margin-top", value)
}
fun StyleBuilder.padding(value: CSSUnitValue) {
fun StyleBuilder.padding(value: CSSNumeric) {
// padding hasn't Typed OM yet
property("padding", value)
}

3
web/core/src/jsMain/kotlin/androidx/compose/web/css/CSSUnits.kt

@ -2,7 +2,7 @@
package org.jetbrains.compose.web.css
external interface CSSNumericValue<T : CSSUnit> : StylePropertyValue
external interface CSSNumericValue<T : CSSUnit> : StylePropertyValue, CSSVariableValueAs<CSSNumericValue<T>>
external interface CSSSizeValue<T : CSSUnit> : CSSNumericValue<T> {
val value: Float
@ -32,6 +32,7 @@ typealias CSSLengthOrPercentageValue = CSSSizeValue<out CSSUnitLengthOrPercentag
typealias CSSLengthValue = CSSSizeValue<out CSSUnitLength>
typealias CSSPercentageValue = CSSSizeValue<out CSSUnitPercentage>
typealias CSSUnitValue = CSSSizeValue<out CSSUnit>
typealias CSSNumeric = CSSNumericValue<out CSSUnit>
typealias CSSpxValue = CSSSizeValue<CSSUnit.px>
// fake interfaces to distinguish units

4
web/core/src/jsMain/kotlin/androidx/compose/web/css/Color.kt

@ -2,7 +2,9 @@
package org.jetbrains.compose.web.css
abstract class Color : CSSStyleValue {
external interface CSSColorValue: StylePropertyValue, CSSVariableValueAs<CSSColorValue>
abstract class Color : CSSStyleValue, CSSColorValue {
data class Named(val value: String) : Color() {
override fun toString(): String = value
}

24
web/core/src/jsMain/kotlin/androidx/compose/web/css/StyleBuilder.kt

@ -34,19 +34,13 @@ interface StyleBuilder {
inline fun variableValue(variableName: String, fallback: StylePropertyValue? = null) =
"var(--$variableName${fallback?.let { ", $it" } ?: ""})"
external interface CSSVariableValue<TValue> : StylePropertyValue
external interface CSSVariableValueAs<out T: StylePropertyValue>: StylePropertyValue
inline fun <TValue> CSSVariableValue(value: String) =
StylePropertyValue(value).unsafeCast<CSSVariableValue<TValue>>()
inline fun <TValue> CSSVariableValue(value: StylePropertyValue) =
value.unsafeCast<TValue>()
//fun <TValue> CSSVariableValue(value: Number) =
// value.unsafeCast<CSSVariableValue<TValue>>()
//
//fun <TValue : CSSStyleValue> CSSVariableValue(value: TValue) =
// value.unsafeCast<CSSVariableValue<TValue>>()
//
//fun <TValue> CSSVariableValue(value: StylePropertyValue) =
// value.unsafeCast<CSSVariableValue<TValue>>()
inline fun <TValue> CSSVariableValue(value: String) =
CSSVariableValue<TValue>(StylePropertyValue(value))
// after adding `variable` word `add` became ambiguous
@Deprecated(
@ -74,6 +68,14 @@ fun <TValue: StylePropertyValue> CSSStyleVariable<TValue>.value(fallback: TValue
)
)
fun <TValue: CSSVariableValueAs<TValue>> CSSStyleVariable<TValue>.value(fallback: TValue? = null) =
CSSVariableValue<TValue>(
variableValue(
name,
fallback
)
)
fun <TValue: StylePropertyValue> CSSVariables.variable() =
ReadOnlyProperty<Any?, CSSStyleVariable<TValue>> { _, property ->
CSSStyleVariable(property.name)

191
web/core/src/jsTest/kotlin/CSSUnitApiTests.kt

@ -5,40 +5,7 @@
package org.jetbrains.compose.web.core.tests
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import org.jetbrains.compose.web.css.ch
import org.jetbrains.compose.web.css.cm
import org.jetbrains.compose.web.css.cssRem
import org.jetbrains.compose.web.css.deg
import org.jetbrains.compose.web.css.div
import org.jetbrains.compose.web.css.dpcm
import org.jetbrains.compose.web.css.dpi
import org.jetbrains.compose.web.css.dppx
import org.jetbrains.compose.web.css.em
import org.jetbrains.compose.web.css.fr
import org.jetbrains.compose.web.css.grad
import org.jetbrains.compose.web.css.left
import org.jetbrains.compose.web.css.minus
import org.jetbrains.compose.web.css.mm
import org.jetbrains.compose.web.css.ms
import org.jetbrains.compose.web.css.number
import org.jetbrains.compose.web.css.pc
import org.jetbrains.compose.web.css.percent
import org.jetbrains.compose.web.css.plus
import org.jetbrains.compose.web.css.pt
import org.jetbrains.compose.web.css.px
import org.jetbrains.compose.web.css.rad
import org.jetbrains.compose.web.css.s
import org.jetbrains.compose.web.css.times
import org.jetbrains.compose.web.css.top
import org.jetbrains.compose.web.css.turn
import org.jetbrains.compose.web.css.vh
import org.jetbrains.compose.web.css.vmax
import org.jetbrains.compose.web.css.vmin
import org.jetbrains.compose.web.css.vw
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import org.w3c.dom.HTMLElement
import org.w3c.dom.get
@ -306,6 +273,162 @@ class CSSUnitApiTests {
assertEquals(1.fr, 7.fr - 4.fr - 2.fr)
}
@Test
fun calcMultiplicationLeft() {
val typedResultAbs: CSSNumericValue<CSSUnitAbs> = ((4.pt + 4.px) * 4)
assertEquals("calc((4pt + 4px) * 4)", typedResultAbs.toString())
val typedResultRel: CSSNumericValue<CSSUnitRel> = ((4.ex + 4.em) * 4)
assertEquals("calc((4ex + 4em) * 4)", typedResultRel.toString())
val typedResultLength: CSSNumericValue<CSSUnitLength> = ((4.pt + 4.em) * 4)
assertEquals("calc((4pt + 4em) * 4)", typedResultLength.toString())
val typedResultLengthOrPercentage: CSSNumericValue<CSSUnitLengthOrPercentage> = ((4.percent + 4.px) * 4)
assertEquals("calc((4% + 4px) * 4)", typedResultLengthOrPercentage.toString())
val typedResultAngle: CSSNumericValue<CSSUnitAngle> = ((4.deg + 4.grad) * 4)
assertEquals("calc((4deg + 4grad) * 4)", typedResultAngle.toString())
val typedResultTime: CSSNumericValue<CSSUnitTime> = ((4.s + 4.ms) * 4)
assertEquals("calc((4s + 4ms) * 4)", typedResultTime.toString())
val typedResultFrequency: CSSNumericValue<CSSUnitFrequency> = ((4.Hz + 4.kHz) * 4)
assertEquals("calc((4Hz + 4kHz) * 4)", typedResultFrequency.toString())
val typedResultResolution: CSSNumericValue<CSSUnitResolution> = ((4.dpi + 4.dppx) * 4)
assertEquals("calc((4dpi + 4dppx) * 4)", typedResultResolution.toString())
}
@Test
fun calcDivisionLeft() {
val typedResultAbs: CSSNumericValue<CSSUnitAbs> = ((4.pt + 4.px) / 4)
assertEquals("calc((4pt + 4px) / 4)", typedResultAbs.toString())
val typedResultRel: CSSNumericValue<CSSUnitRel> = ((4.ex + 4.em) / 4)
assertEquals("calc((4ex + 4em) / 4)", typedResultRel.toString())
val typedResultLength: CSSNumericValue<CSSUnitLength> = ((4.pt + 4.em) / 4)
assertEquals("calc((4pt + 4em) / 4)", typedResultLength.toString())
val typedResultLengthOrPercentage: CSSNumericValue<CSSUnitLengthOrPercentage> = ((4.percent + 4.px) / 4)
assertEquals("calc((4% + 4px) / 4)", typedResultLengthOrPercentage.toString())
val typedResultAngle: CSSNumericValue<CSSUnitAngle> = ((4.deg + 4.grad) / 4)
assertEquals("calc((4deg + 4grad) / 4)", typedResultAngle.toString())
val typedResultTime: CSSNumericValue<CSSUnitTime> = ((4.s + 4.ms) / 4)
assertEquals("calc((4s + 4ms) / 4)", typedResultTime.toString())
val typedResultFrequency: CSSNumericValue<CSSUnitFrequency> = ((4.Hz + 4.kHz) / 4)
assertEquals("calc((4Hz + 4kHz) / 4)", typedResultFrequency.toString())
val typedResultResolution: CSSNumericValue<CSSUnitResolution> = ((4.dpi + 4.dppx) / 4)
assertEquals("calc((4dpi + 4dppx) / 4)", typedResultResolution.toString())
}
@Test
fun calcMultiplicationRight() {
val typedResultAbs: CSSNumericValue<CSSUnitAbs> = (4 * (4.pt + 4.px))
assertEquals("calc(4 * (4pt + 4px))", typedResultAbs.toString())
val typedResultRel: CSSNumericValue<CSSUnitRel> = (4 * (4.ex + 4.em))
assertEquals("calc(4 * (4ex + 4em))", typedResultRel.toString())
val typedResultLength: CSSNumericValue<CSSUnitLength> = (4 * (4.pt + 4.em))
assertEquals("calc(4 * (4pt + 4em))", typedResultLength.toString())
val typedResultLengthOrPercentage: CSSNumericValue<CSSUnitLengthOrPercentage> = (4 * (4.percent + 4.px))
assertEquals("calc(4 * (4% + 4px))", typedResultLengthOrPercentage.toString())
val typedResultAngle: CSSNumericValue<CSSUnitAngle> = (4 * (4.deg + 4.grad))
assertEquals("calc(4 * (4deg + 4grad))", typedResultAngle.toString())
val typedResultTime: CSSNumericValue<CSSUnitTime> = (4 * (4.s + 4.ms))
assertEquals("calc(4 * (4s + 4ms))", typedResultTime.toString())
val typedResultFrequency: CSSNumericValue<CSSUnitFrequency> = (4 * (4.Hz + 4.kHz))
assertEquals("calc(4 * (4Hz + 4kHz))", typedResultFrequency.toString())
val typedResultResolution: CSSNumericValue<CSSUnitResolution> = (4 * (4.dpi + 4.dppx))
assertEquals("calc(4 * (4dpi + 4dppx))", typedResultResolution.toString())
}
@Test
fun calcAdd() {
val typedResultAbs: CSSNumericValue<CSSUnitAbs> = 4.pt + 4.px
assertEquals("calc(4pt + 4px)", typedResultAbs.toString())
val typedResultRel: CSSNumericValue<CSSUnitRel> = 4.ex + 4.em
assertEquals("calc(4ex + 4em)", typedResultRel.toString())
val typedResultLength: CSSNumericValue<CSSUnitLength> = 4.pt + 4.em
assertEquals("calc(4pt + 4em)", typedResultLength.toString())
val typedResultLengthOrPercentage: CSSNumericValue<CSSUnitLengthOrPercentage> = 4.percent + 4.px
assertEquals("calc(4% + 4px)", typedResultLengthOrPercentage.toString())
val typedResultAngle: CSSNumericValue<CSSUnitAngle> = 4.deg + 4.grad
assertEquals("calc(4deg + 4grad)", typedResultAngle.toString())
val typedResultTime: CSSNumericValue<CSSUnitTime> = 4.s + 4.ms
assertEquals("calc(4s + 4ms)", typedResultTime.toString())
val typedResultFrequency: CSSNumericValue<CSSUnitFrequency> = 4.Hz + 4.kHz
assertEquals("calc(4Hz + 4kHz)", typedResultFrequency.toString())
val typedResultResolution: CSSNumericValue<CSSUnitResolution> = 4.dpi + 4.dppx
assertEquals("calc(4dpi + 4dppx)", typedResultResolution.toString())
}
@Test
fun calcSubstract() {
val typedResultAbs: CSSNumericValue<CSSUnitAbs> = 4.pt - 4.px
assertEquals("calc(4pt - 4px)", typedResultAbs.toString())
val typedResultRel: CSSNumericValue<CSSUnitRel> = 4.ex - 4.em
assertEquals("calc(4ex - 4em)", typedResultRel.toString())
val typedResultLength: CSSNumericValue<CSSUnitLength> = 4.pt - 4.em
assertEquals("calc(4pt - 4em)", typedResultLength.toString())
val typedResultLengthOrPercentage: CSSNumericValue<CSSUnitLengthOrPercentage> = 4.percent - 4.px
assertEquals("calc(4% - 4px)", typedResultLengthOrPercentage.toString())
val typedResultAngle: CSSNumericValue<CSSUnitAngle> = 4.deg - 4.grad
assertEquals("calc(4deg - 4grad)", typedResultAngle.toString())
val typedResultTime: CSSNumericValue<CSSUnitTime> = 4.s - 4.ms
assertEquals("calc(4s - 4ms)", typedResultTime.toString())
val typedResultFrequency: CSSNumericValue<CSSUnitFrequency> = 4.Hz - 4.kHz
assertEquals("calc(4Hz - 4kHz)", typedResultFrequency.toString())
val typedResultResolution: CSSNumericValue<CSSUnitResolution> = 4.dpi - 4.dppx
assertEquals("calc(4dpi - 4dppx)", typedResultResolution.toString())
}
@Test
fun calcAssociative() {
val typedResultLengthLeft: CSSNumericValue<CSSUnitLength> = 4.pt - 4.px + 4.em
assertEquals("calc((4pt - 4px) + 4em)", typedResultLengthLeft.toString())
val typedResultLengthRight: CSSNumericValue<CSSUnitLength> = 4.pt - (4.px + 4.em)
assertEquals("calc(4pt - (4px + 4em))", typedResultLengthRight.toString())
}
@Test
fun calcVaraiables() {
val variables = object : CSSVariables {
val pxVar by variable<CSSSizeValue<CSSUnit.px>>()
}
val typedResultLength: CSSNumericValue<CSSUnitLength> = 4.pt + variables.pxVar.value()
assertEquals("calc(4pt + var(--pxVar))", typedResultLength.toString())
val typedResultLengthFallback: CSSNumericValue<CSSUnitLength> = 4.pt + variables.pxVar.value(4.px)
assertEquals("calc(4pt + var(--pxVar, 4px))", typedResultLengthFallback.toString())
}
@Test
fun staticEvaluation() = runTest {
composition {

Loading…
Cancel
Save