Browse Source

Add opportunity to use custom prefixes in StyleSheet (#3015)

pull/4215/head
InsanusMokrassar 3 months ago committed by GitHub
parent
commit
d9d4bf3496
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 29
      html/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt
  2. 49
      html/core/src/jsTest/kotlin/css/AnimationTests.kt
  3. 30
      html/core/src/jsTest/kotlin/css/StyleSheetTests.kt

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

@ -20,6 +20,9 @@ class CSSRulesHolderState : CSSRulesHolder {
/**
* Represents a collection of the css style rules.
* StyleSheet needs to be mounted.
*
* @param customPrefix Will be used as prefix with current style. Pass `null` to use default value (classname of realization)
*
* @see [Style]
*
* Example:
@ -38,12 +41,22 @@ class CSSRulesHolderState : CSSRulesHolder {
* ```
*/
open class StyleSheet(
customPrefix: String?,
private val rulesHolder: CSSRulesHolder = CSSRulesHolderState(),
val usePrefix: Boolean = true,
) : StyleSheetBuilder, CSSRulesHolder by rulesHolder {
private val boundClasses = mutableMapOf<String, CSSRuleDeclarationList>()
protected val prefix: String = customPrefix ?: "${this::class.simpleName}-"
val usePrefix: Boolean = customPrefix == null
constructor(
rulesHolder: CSSRulesHolder = CSSRulesHolderState(),
usePrefix: Boolean = true
) : this(
if (usePrefix) null else "",
rulesHolder
)
protected fun style(cssRule: CSSBuilder.() -> Unit) = CSSHolder(usePrefix, cssRule)
protected fun style(cssRule: CSSBuilder.() -> Unit) = CSSHolder(prefix, cssRule)
/**
* Example:
@ -69,7 +82,7 @@ open class StyleSheet(
* }
* ```
*/
protected fun keyframes(cssKeyframes: CSSKeyframesBuilder.() -> Unit) = CSSKeyframesHolder(usePrefix, cssKeyframes)
protected fun keyframes(cssKeyframes: CSSKeyframesBuilder.() -> Unit) = CSSKeyframesHolder(prefix, cssKeyframes)
companion object {
private var counter = 0
@ -88,13 +101,12 @@ open class StyleSheet(
}
}
protected class CSSHolder(private val usePrefix: Boolean, private val cssBuilder: CSSBuilder.() -> Unit) {
protected class CSSHolder(private val prefix: String, private val cssBuilder: CSSBuilder.() -> Unit) {
operator fun provideDelegate(
sheet: StyleSheet,
property: KProperty<*>
): ReadOnlyProperty<Any?, String> {
val sheetName = if (usePrefix) "${sheet::class.simpleName}-" else ""
val className = "$sheetName${property.name}"
val className = "$prefix${property.name}"
val selector = object : CSSSelector() {
override fun asString() = ".${className}"
}
@ -110,15 +122,14 @@ open class StyleSheet(
* See [keyframes]
*/
protected class CSSKeyframesHolder(
private val usePrefix: Boolean,
private val prefix: String,
private val keyframesBuilder: CSSKeyframesBuilder.() -> Unit
) {
operator fun provideDelegate(
sheet: StyleSheet,
property: KProperty<*>
): ReadOnlyProperty<Any?, CSSNamedKeyframes> {
val sheetName = if (usePrefix) "${sheet::class.simpleName}-" else ""
val keyframesName = "$sheetName${property.name}"
val keyframesName = "$prefix${property.name}"
val rule = buildKeyframes(keyframesName, keyframesBuilder)
sheet.add(rule)

49
html/core/src/jsTest/kotlin/css/AnimationTests.kt

@ -35,6 +35,28 @@ object AnimationsStyleSheet : StyleSheet() {
}
}
class AnimationsStyleSheetWithCustomPrefix(
customPrefix: String
) : StyleSheet(customPrefix) {
val bounce by keyframes {
from {
property("transform", "translateX(50%)")
}
to {
property("transform", "translateX(-50%)")
}
}
val animationClass by style {
animation(bounce) {
duration(2.s)
timingFunction(AnimationTimingFunction.EaseIn)
direction(AnimationDirection.Alternate)
}
}
}
@ExperimentalComposeWebApi
class AnimationTests {
@Test
@ -76,4 +98,31 @@ class AnimationTests {
"Animation class wasn't injected correctly"
)
}
@Test
fun animationClassInjectedWithCustomPrefix() = runTest {
val customPrefix = "CustomPrefix-"
composition {
Style(AnimationsStyleSheetWithCustomPrefix(customPrefix))
}
val el = nextChild() as HTMLStyleElement
val cssRules = (el.sheet as? CSSStyleSheet)?.cssRules
val rules = (0 until (cssRules?.length ?: 0)).map {
cssRules?.item(it)?.cssText?.replace("\n", "") ?: ""
}
// TODO: we need to come up with test that not relying on any kind of formatting
assertEquals(
"@keyframes ${customPrefix}bounce {0% { transform: translateX(50%); }100% { transform: translateX(-50%); }}",
rules[0].replace(" 0%", "0%").replace(" 100%", "100%"),
"Animation keyframes wasn't injected correctly"
)
assertEquals(
".${customPrefix}animationClass { animation: 2s ease-in 0s 1 alternate none running ${customPrefix}bounce; }".trimIndent(),
rules[1],
"Animation class wasn't injected correctly"
)
}
}

30
html/core/src/jsTest/kotlin/css/StyleSheetTests.kt

@ -42,4 +42,34 @@ class StyleSheetTests {
)
}
@Test
fun stylesheetCorrectlyUsingIncomingPrefix() {
val testPrefixParent = "test_prefix_parent-"
val testPrefixChild = "test_prefix_child-"
val styleSheet = object : StyleSheet(customPrefix = testPrefixParent) {
val someClassName by style {
color(Color.red)
}
}
val childStyleSheet = object : StyleSheet(customPrefix = testPrefixChild, styleSheet) {
val someClassName by style {
color(Color.green)
}
}
assertContentEquals(
listOf(".${testPrefixParent}someClassName { color: red;}", ".${testPrefixChild}someClassName { color: green;}"),
styleSheet.serializeRules(),
"styleSheet rules"
)
assertContentEquals(
listOf(".${testPrefixParent}someClassName { color: red;}", ".${testPrefixChild}someClassName { color: green;}"),
childStyleSheet.serializeRules(),
"childStyleSheet rules"
)
}
}
Loading…
Cancel
Save