Browse Source

Add implicit self for nested css selector (#1284)

* Deprecate `descendant` and introduce `desc` util to combine selectors

* Add internal `contains` method to `CSSSelector`

* Add implicit self for nested css selector
pull/1305/head v1.0.0-beta1
Abasov Akif 3 years ago committed by GitHub
parent
commit
14bf6674d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSBuilder.kt
  2. 51
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/selectors/CSSSelectors.kt
  3. 46
      web/core/src/jsTest/kotlin/CSSStylesheetTests.kt

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

@ -1,6 +1,7 @@
package org.jetbrains.compose.web.css
import org.jetbrains.compose.web.css.selectors.CSSSelector
import org.jetbrains.compose.web.css.selectors.desc
interface CSSBuilder : CSSStyleRuleBuilder, GenericStyleSheetBuilder<CSSBuilder> {
val root: CSSSelector
@ -15,7 +16,11 @@ class CSSBuilderImpl(
override fun style(selector: CSSSelector, cssRule: CSSBuilder.() -> Unit) {
val (style, rules) = buildCSS(root, selector, cssRule)
rules.forEach { add(it) }
add(selector, style)
if (selector.contains(self, true) || selector.contains(root, true)) {
add(selector, style)
} else {
add(desc(self, selector), style)
}
}
override fun buildRules(rulesBuild: GenericStyleSheetBuilder<CSSBuilder>.() -> Unit) =

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

@ -23,7 +23,11 @@ sealed class Nth {
open class CSSSelector {
override fun equals(other: Any?): Boolean {
return toString() == other.toString()
return this === other || toString() == other.toString()
}
internal open fun contains(other: CSSSelector, strict: Boolean = false): Boolean {
return if (strict) this === other else this == other
}
data class Raw(val selector: String) : CSSSelector() {
@ -69,26 +73,44 @@ open class CSSSelector {
}
data class Combine(val selectors: MutableList<CSSSelector>) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, selectors, strict)
override fun toString(): String = selectors.joinToString("")
}
data class Group(val selectors: List<CSSSelector>) : CSSSelector() {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, selectors, strict)
override fun toString(): String = selectors.joinToString(", ")
}
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 toString(): String = "$parent $selected"
}
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 toString(): String = "$parent > $selected"
}
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 toString(): String = "$prev ~ $selected"
}
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 toString(): String = "$prev + $selected"
}
@ -177,11 +199,17 @@ open class CSSSelector {
override fun argsStr() = "$nth"
}
class Host(val selector: CSSSelector) : PseudoClass("host") {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(selector), strict)
override fun argsStr() = "$selector"
}
// Etc
class Not(val selector: CSSSelector) : PseudoClass("not") {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(selector), strict)
override fun argsStr() = "$selector"
}
}
@ -207,6 +235,9 @@ open class CSSSelector {
}
class Slotted(val selector: CSSSelector) : PseudoElement("slotted") {
override fun contains(other: CSSSelector, strict: Boolean): Boolean =
contains(this, other, listOf(selector), strict)
override fun argsStr() = selector.toString()
}
}
@ -231,8 +262,19 @@ fun attr(
caseSensitive: Boolean = true
) = CSSSelector.Attribute(name, value, operator, caseSensitive)
fun group(vararg selectors: CSSSelector) = CSSSelector.Group(selectors.toList())
@Deprecated("Replaced with `desc`", ReplaceWith("desc(parent, selected)"))
fun descendant(parent: CSSSelector, selected: CSSSelector) =
desc(parent, selected)
fun desc(parent: CSSSelector, selected: CSSSelector) =
CSSSelector.Descendant(parent, selected)
fun desc(parent: CSSSelector, selected: String) =
desc(parent, selector(selected))
fun desc(parent: String, selected: CSSSelector) =
desc(selector(parent), selected)
fun desc(parent: String, selected: String) =
desc(selector(parent), selector(selected))
fun child(parent: CSSSelector, selected: CSSSelector) =
CSSSelector.Child(parent, selected)
fun sibling(sibling: CSSSelector, selected: CSSSelector) = CSSSelector.Descendant(sibling, selected)
@ -241,3 +283,10 @@ fun adjacent(sibling: CSSSelector, selected: CSSSelector) = CSSSelector.Adjacent
fun not(selector: CSSSelector) = CSSSelector.PseudoClass.Not(selector)
fun hover() = CSSSelector.PseudoClass.hover
fun hover(selector: CSSSelector) = selector + hover()
@Suppress("SuspiciousEqualsCombination")
private 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
}

46
web/core/src/jsTest/kotlin/CSSStylesheetTests.kt

@ -7,12 +7,15 @@ package org.jetbrains.compose.web.core.tests
import kotlinx.browser.window
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.css.selectors.desc
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.stringPresentation
import org.w3c.dom.HTMLElement
import org.w3c.dom.get
import kotlin.test.Test
import kotlin.test.assertEquals
import org.jetbrains.compose.web.testutils.*
import kotlin.test.assertContains
object AppCSSVariables {
val width by variable<CSSUnitValue>()
@ -68,6 +71,19 @@ object AppStylesheet : StyleSheet() {
}
}
val withNestedWithImplicitSelf by style {
color(Color.green)
"h1" {
color(Color.lime)
}
}
val withNestedWithExplicitSelf by style {
color(Color.green)
desc(self, "h1") style {
color(Color.lime)
}
}
}
@ -144,4 +160,34 @@ class CSSVariableTests {
assertEquals("rgb(0, 128, 0)", window.getComputedStyle(el).backgroundColor)
}
}
@Test
fun nestedStyleWithImplicitSelf() = runTest {
val generatedRules = AppStylesheet.cssRules.map { it.stringPresentation() }
assertContains(
generatedRules,
"""
.AppStylesheet-withNestedWithImplicitSelf h1 {
color: lime;
}
""".trimIndent(),
"Nested selector with implicit self isn't generated correctly"
)
}
@Test
fun nestedStyleWithExplicitSelf() = runTest {
val generatedRules = AppStylesheet.cssRules.map { it.stringPresentation() }
assertContains(
generatedRules,
"""
.AppStylesheet-withNestedWithExplicitSelf h1 {
color: lime;
}
""".trimIndent(),
"Nested selector with implicit self isn't generated correctly"
)
}
}

Loading…
Cancel
Save