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. * Represents a collection of the css style rules.
* StyleSheet needs to be mounted. * 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] * @see [Style]
* *
* Example: * Example:
@ -38,12 +41,22 @@ class CSSRulesHolderState : CSSRulesHolder {
* ``` * ```
*/ */
open class StyleSheet( open class StyleSheet(
customPrefix: String?,
private val rulesHolder: CSSRulesHolder = CSSRulesHolderState(), private val rulesHolder: CSSRulesHolder = CSSRulesHolderState(),
val usePrefix: Boolean = true,
) : StyleSheetBuilder, CSSRulesHolder by rulesHolder { ) : StyleSheetBuilder, CSSRulesHolder by rulesHolder {
private val boundClasses = mutableMapOf<String, CSSRuleDeclarationList>() 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: * 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 { companion object {
private var counter = 0 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( operator fun provideDelegate(
sheet: StyleSheet, sheet: StyleSheet,
property: KProperty<*> property: KProperty<*>
): ReadOnlyProperty<Any?, String> { ): ReadOnlyProperty<Any?, String> {
val sheetName = if (usePrefix) "${sheet::class.simpleName}-" else "" val className = "$prefix${property.name}"
val className = "$sheetName${property.name}"
val selector = object : CSSSelector() { val selector = object : CSSSelector() {
override fun asString() = ".${className}" override fun asString() = ".${className}"
} }
@ -110,15 +122,14 @@ open class StyleSheet(
* See [keyframes] * See [keyframes]
*/ */
protected class CSSKeyframesHolder( protected class CSSKeyframesHolder(
private val usePrefix: Boolean, private val prefix: String,
private val keyframesBuilder: CSSKeyframesBuilder.() -> Unit private val keyframesBuilder: CSSKeyframesBuilder.() -> Unit
) { ) {
operator fun provideDelegate( operator fun provideDelegate(
sheet: StyleSheet, sheet: StyleSheet,
property: KProperty<*> property: KProperty<*>
): ReadOnlyProperty<Any?, CSSNamedKeyframes> { ): ReadOnlyProperty<Any?, CSSNamedKeyframes> {
val sheetName = if (usePrefix) "${sheet::class.simpleName}-" else "" val keyframesName = "$prefix${property.name}"
val keyframesName = "$sheetName${property.name}"
val rule = buildKeyframes(keyframesName, keyframesBuilder) val rule = buildKeyframes(keyframesName, keyframesBuilder)
sheet.add(rule) 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 @ExperimentalComposeWebApi
class AnimationTests { class AnimationTests {
@Test @Test
@ -76,4 +98,31 @@ class AnimationTests {
"Animation class wasn't injected correctly" "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