Browse Source

Simplify inner CSS API, remove css fun completely (was experimental, will be redesigned)

pull/1889/head
Shagen Ogandzhanian 2 years ago committed by GitHub
parent
commit
cd7691a53f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt
  2. 2
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSBuilder.kt
  3. 44
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt
  4. 80
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheetBuilder.kt
  5. 18
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/selectors/CSSSelectors.kt
  6. 8
      web/core/src/jsTest/kotlin/elements/AttributesTests.kt
  7. 10
      web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/Sample.kt

13
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt

@ -158,6 +158,12 @@ fun AttrsScope<HTMLInputElement>.height(value: Int) =
fun AttrsScope<HTMLInputElement>.width(value: Int) =
attr("width", value.toString()) // image only
fun AttrsScope<HTMLCanvasElement>.width(value: Int) =
attr("width", value.toString())
fun AttrsScope<HTMLCanvasElement>.height(value: Int) =
attr("height", value.toString())
fun AttrsScope<HTMLInputElement>.list(dataListId: String) =
attr("list", dataListId)
@ -330,10 +336,3 @@ fun AttrsScope<HTMLTableCellElement>.colspan(value: Int): AttrsScope<HTMLTableCe
fun AttrsScope<HTMLTableCellElement>.rowspan(value: Int): AttrsScope<HTMLTableCellElement> =
attr("rowspan", value.toString())
/* Canvas attributes */
fun AttrsScope<HTMLCanvasElement>.width(value: CSSSizeValue<out CSSUnitLengthOrPercentage>) =
attr("width", value.toString())
fun AttrsScope<HTMLCanvasElement>.height(value: CSSSizeValue<out CSSUnitLengthOrPercentage>) =
attr("height", value.toString())

2
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSBuilder.kt

@ -12,7 +12,7 @@ class CSSBuilderImpl(
rulesHolder: CSSRulesHolder
) : CSSRuleBuilderImpl(), CSSBuilder, CSSRulesHolder by rulesHolder {
override fun style(selector: CSSSelector, cssRule: CSSBuilder.() -> Unit) {
val resolvedSelector = if (selector.contains(self, true) || selector.contains(currentRoot, true)) {
val resolvedSelector = if (selector.contains(self) || selector.contains(currentRoot)) {
selector
} else {
desc(self, selector)

44
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt

@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import org.jetbrains.compose.web.ExperimentalComposeWebStyleApi
import org.jetbrains.compose.web.css.selectors.CSSSelector
import org.jetbrains.compose.web.dom.Style
import kotlin.properties.ReadOnlyProperty
@ -79,49 +78,32 @@ open class StyleSheet(
@Suppress("EqualsOrHashCode")
internal class CSSSelfSelector(var selector: CSSSelector? = null) : CSSSelector() {
override fun toString(): String = throw IllegalStateException("You can't concatenate `String + CSSSelector` which contains `self` or `root`. Use `selector(<your string>)` to convert `String` to `CSSSelector` for proper work. https://github.com/JetBrains/compose-jb/issues/1440")
override fun asString(): String = selector?.asString() ?: throw IllegalStateException("You can't instantiate self")
override fun toString(): String =
throw IllegalStateException("You can't concatenate `String + CSSSelector` which contains `self` or `root`. Use `selector(<your string>)` to convert `String` to `CSSSelector` for proper work. https://github.com/JetBrains/compose-jb/issues/1440")
override fun asString(): String =
selector?.asString() ?: throw IllegalStateException("You can't instantiate self")
override fun equals(other: Any?): Boolean {
return other is CSSSelfSelector
}
}
// TODO: just proof of concept, do not use it
@ExperimentalComposeWebStyleApi
fun css(cssBuild: CSSBuilder.() -> Unit): String {
val selfSelector = CSSSelfSelector()
val (style, newCssRules) = buildCSS(selfSelector, selfSelector, cssBuild)
val cssRule = cssRules.find {
it is CSSStyleRuleDeclaration &&
it.selector is CSSSelector.CSSClass && it.style == style &&
(boundClasses[it.selector.className] ?: emptyList()) == newCssRules
}.unsafeCast<CSSStyleRuleDeclaration?>()
return if (cssRule != null) {
cssRule.selector.unsafeCast<CSSSelector.CSSClass>().className
} else {
val classNameSelector = CSSSelector.CSSClass("auto-${counter++}")
selfSelector.selector = classNameSelector
add(classNameSelector, style)
newCssRules.forEach { add(it) }
boundClasses[classNameSelector.className] = newCssRules
classNameSelector.className
}
}
protected class CSSHolder(private val usePrefix: Boolean, private val cssBuilder: CSSBuilder.() -> Unit) {
operator fun provideDelegate(
sheet: StyleSheet,
property: KProperty<*>
): ReadOnlyProperty<Any?, String> {
val sheetName = if (usePrefix) "${sheet::class.simpleName}-" else ""
val selector = CSSSelector.CSSClass("$sheetName${property.name}")
val className = "$sheetName${property.name}"
val selector = object : CSSSelector() {
override fun asString() = ".${className}"
}
val (properties, rules) = buildCSS(selector, selector, cssBuilder)
sheet.add(selector, properties)
rules.forEach { sheet.add(it) }
return ReadOnlyProperty { _, _ ->
selector.className
}
return ReadOnlyProperty { _, _ -> className }
}
}
@ -158,9 +140,9 @@ internal fun buildCSS(
): Pair<StyleHolder, CSSRuleDeclarationList> {
val styleSheet = StyleSheetBuilderImpl()
// workaround because of problems with plus operator overloading
val root = if (thisClass is StyleSheet.CSSSelfSelector) thisClass else StyleSheet.CSSSelfSelector(thisClass)
val root = (thisClass as? StyleSheet.CSSSelfSelector) ?: StyleSheet.CSSSelfSelector(thisClass)
// workaround because of problems with plus operator overloading
val self = if (thisContext is StyleSheet.CSSSelfSelector) thisContext else StyleSheet.CSSSelfSelector(thisContext)
val self = (thisContext as? StyleSheet.CSSSelfSelector) ?: StyleSheet.CSSSelfSelector(thisContext)
val builder = CSSBuilderImpl(root, self, styleSheet)
builder.cssRule()

80
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheetBuilder.kt

@ -26,24 +26,25 @@ interface GenericStyleSheetBuilder<TBuilder> : CSSRulesHolder, SelectorsScope {
}
operator fun String.invoke(cssRule: TBuilder.() -> Unit) {
style(Raw(this), cssRule)
style(RawSelector(this), cssRule)
}
infix fun String.style(cssRule: TBuilder.() -> Unit) {
style(Raw(this), cssRule)
style(RawSelector(this), cssRule)
}
}
private val Universal = RawSelector("*")
interface SelectorsScope {
fun selector(selector: String): CSSSelector = Raw(selector)
fun selector(selector: String): CSSSelector = RawSelector(selector)
fun combine(vararg selectors: CSSSelector): CSSSelector = Combine(selectors.toMutableList())
operator fun CSSSelector.plus(selector: CSSSelector): CSSSelector {
if (this is Combine) {
return if (this is Combine) {
this.selectors.add(selector)
return this
}
return if (selector is Combine) {
this
} else if (selector is Combine) {
selector.selectors.add(0, this)
selector
} else {
@ -52,12 +53,12 @@ interface SelectorsScope {
}
operator fun CSSSelector.plus(selector: String): CSSSelector {
if (this is Combine) {
return if (this is Combine) {
this.selectors.add(selector(selector))
return this
this
} else {
combine(this, selector(selector))
}
return combine(this, selector(selector))
}
@JsName("returnUniversalSelector")
@ -67,9 +68,9 @@ interface SelectorsScope {
val universal: CSSSelector
get() = Universal
fun type(type: String): CSSSelector = Type(type)
fun className(className: String): CSSSelector = CSSSelector.CSSClass(className)
fun id(id: String): CSSSelector = Id(id)
fun type(type: String): CSSSelector = RawSelector(type)
fun className(className: String): CSSSelector = RawSelector(".$className")
fun id(id: String): CSSSelector = RawSelector("#$id")
fun attr(
name: String,
@ -241,33 +242,21 @@ interface SelectorsScope {
fun slotted(selector: CSSSelector): CSSSelector = PseudoElementInternal.Slotted(selector)
}
private data class Id(val id: String) : CSSSelector() {
override fun toString(): String = "#$id"
}
private data class Type(val type: String) : CSSSelector() {
override fun toString(): String = type
}
private object Universal : CSSSelector() {
override fun toString(): String = "*"
}
private data class Raw(val selector: String) : CSSSelector() {
private data class RawSelector(val selector: String) : CSSSelector() {
override fun toString(): String = selector
}
private data class Combine(val selectors: MutableList<CSSSelector>) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, selectors, strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, selectors)
override fun toString(): String = selectors.joinToString("")
override fun asString(): String = selectors.joinToString("") { it.asString() }
}
private data class Group(val selectors: List<CSSSelector>) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, selectors, strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, selectors)
override fun toString(): String = selectors.joinToString(", ")
override fun asString(): String = selectors.joinToString(", ") { it.asString() }
@ -275,32 +264,32 @@ private data class Group(val selectors: List<CSSSelector>) : CSSSelector() {
private data class Descendant(val parent: CSSSelector, val selected: CSSSelector) :
CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(parent, selected), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(parent, selected))
override fun toString(): String = "$parent $selected"
override fun asString(): String = "${parent.asString()} ${selected.asString()}"
}
private data class Child(val parent: CSSSelector, val selected: CSSSelector) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(parent, selected), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(parent, selected))
override fun toString(): String = "$parent > $selected"
override fun asString(): String = "${parent.asString()} > ${selected.asString()}"
}
private data class Sibling(val prev: CSSSelector, val selected: CSSSelector) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(prev, selected), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(prev, selected))
override fun toString(): String = "$prev ~ $selected"
override fun asString(): String = "${prev.asString()} ~ ${selected.asString()}"
}
private data class Adjacent(val prev: CSSSelector, val selected: CSSSelector) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(prev, selected), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(prev, selected))
override fun toString(): String = "$prev + $selected"
override fun asString(): String = "${prev.asString()} + ${selected.asString()}"
@ -327,6 +316,7 @@ private open class PseudoClassInternal(val name: String) : CSSSelector() {
name == other.name && argsStr() == other.argsStr()
} else false
}
open fun argsStr(): String? = null
override fun toString(): String = ":$name${argsStr()?.let { "($it)" } ?: ""}"
@ -353,16 +343,16 @@ private open class PseudoClassInternal(val name: String) : CSSSelector() {
}
class Host internal constructor(val selector: CSSSelector) : PseudoClassInternal("host") {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(selector), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(selector))
override fun argsStr() = selector.asString()
}
// Etc
class Not internal constructor(val selector: CSSSelector) : PseudoClassInternal("not") {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(selector), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(selector))
override fun argsStr() = "$selector"
}
@ -379,8 +369,8 @@ private open class PseudoElementInternal(val name: String) : CSSSelector() {
override fun toString(): String = "::$name${argsStr()?.let { "($it)" } ?: ""}"
class Slotted internal constructor(val selector: CSSSelector) : PseudoElementInternal("slotted") {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(selector), strict)
override fun contains(other: CSSSelector): Boolean =
contains(this, other, listOf(selector))
override fun argsStr() = selector.asString()
}

18
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/selectors/CSSSelectors.kt

@ -35,31 +35,21 @@ sealed interface Nth {
}
abstract class CSSSelector internal constructor() {
override fun equals(other: Any?): Boolean {
return this === other || asString() == (other as? CSSSelector)?.asString()
}
internal open fun contains(other: CSSSelector, strict: Boolean = false): Boolean {
return if (strict) this === other else this == other
internal open fun contains(other: CSSSelector): Boolean {
return this === other
}
@Suppress("SuspiciousEqualsCombination")
protected fun contains(that: CSSSelector, other: CSSSelector, children: List<CSSSelector>, strict: Boolean): Boolean {
return that === other || // exactly same selector
children.any { it.contains(other, strict) } || // contains it in children
(!strict && that == other) // equals structurally
protected fun contains(that: CSSSelector, other: CSSSelector, children: List<CSSSelector>): Boolean {
return (that === other) || children.any { it.contains(other) }
}
// This method made for workaround because of possible concatenation of `String + CSSSelector`,
// so `toString` is called for such operator, but we are calling `asString` for instantiation.
// `toString` is reloaded for CSSSelfSelector
internal open fun asString(): String = toString()
internal data class CSSClass internal constructor(val className: String) : CSSSelector() {
override fun toString(): String = ".$className"
}
object Attribute {
enum class Operator(val value: String) {
Equals("="),

8
web/core/src/jsTest/kotlin/elements/AttributesTests.kt

@ -553,15 +553,15 @@ class AttributesTests {
fun canvasAttributeTest() = runTest {
composition {
Canvas({
height(400.px)
width(450.px)
height(400)
width(450)
})
}
with(nextChild() as HTMLCanvasElement) {
val attrsMap = getAttributeNames().associateWith { getAttribute(it) }
assertEquals(2, attrsMap.size)
assertEquals("450px", attrsMap["width"])
assertEquals("400px", attrsMap["height"])
assertEquals("450", attrsMap["width"])
assertEquals("400", attrsMap["height"])
}
}
}

10
web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/Sample.kt

@ -249,16 +249,8 @@ fun main() {
Div(
attrs = {
classes(
Auto.css {
color(Color.pink)
hover(self) style {
color(Color.blue)
}
}
)
style {
color(Color.pink)
opacity(30.percent)
}
}

Loading…
Cancel
Save