From cd7691a53fc73ed41abda6de604eb1f9a08af48b Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Tue, 22 Feb 2022 18:55:14 +0100 Subject: [PATCH] Simplify inner CSS API, remove css fun completely (was experimental, will be redesigned) --- .../jetbrains/compose/web/attributes/Attrs.kt | 13 ++- .../jetbrains/compose/web/css/CSSBuilder.kt | 2 +- .../jetbrains/compose/web/css/StyleSheet.kt | 44 +++------- .../compose/web/css/StyleSheetBuilder.kt | 80 ++++++++----------- .../compose/web/css/selectors/CSSSelectors.kt | 18 +---- .../jsTest/kotlin/elements/AttributesTests.kt | 8 +- .../androidx/compose/web/sample/Sample.kt | 10 +-- 7 files changed, 64 insertions(+), 111 deletions(-) diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt index 278a3cb371..8cac1bd7d4 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt +++ b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt @@ -158,6 +158,12 @@ fun AttrsScope.height(value: Int) = fun AttrsScope.width(value: Int) = attr("width", value.toString()) // image only +fun AttrsScope.width(value: Int) = + attr("width", value.toString()) + +fun AttrsScope.height(value: Int) = + attr("height", value.toString()) + fun AttrsScope.list(dataListId: String) = attr("list", dataListId) @@ -330,10 +336,3 @@ fun AttrsScope.colspan(value: Int): AttrsScope.rowspan(value: Int): AttrsScope = attr("rowspan", value.toString()) - -/* Canvas attributes */ -fun AttrsScope.width(value: CSSSizeValue) = - attr("width", value.toString()) - -fun AttrsScope.height(value: CSSSizeValue) = - attr("height", value.toString()) diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSBuilder.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSBuilder.kt index ab837611a4..a31f6b50cb 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSBuilder.kt +++ b/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) diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt index 590e5de6da..592b377fbb 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt +++ b/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()` 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()` 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() - return if (cssRule != null) { - cssRule.selector.unsafeCast().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 { 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 { 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() diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheetBuilder.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheetBuilder.kt index 22cdad0009..a8e02eb21f 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheetBuilder.kt +++ b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheetBuilder.kt @@ -26,24 +26,25 @@ interface GenericStyleSheetBuilder : 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() { - 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() { - 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() { 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() } diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/selectors/CSSSelectors.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/selectors/CSSSelectors.kt index 66b9cd652d..c5f34c09b6 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/selectors/CSSSelectors.kt +++ b/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, 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): 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("="), diff --git a/web/core/src/jsTest/kotlin/elements/AttributesTests.kt b/web/core/src/jsTest/kotlin/elements/AttributesTests.kt index 4b83b1ca29..09e7bb2f93 100644 --- a/web/core/src/jsTest/kotlin/elements/AttributesTests.kt +++ b/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"]) } } } diff --git a/web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/Sample.kt b/web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/Sample.kt index 2da2a36295..86603e3556 100644 --- a/web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/Sample.kt +++ b/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) } }