diff --git a/html/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt b/html/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt
index 64f67feb92..3c1bb048c8 100644
--- a/html/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt
+++ b/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()
+ 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 {
- 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 {
- 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)
diff --git a/html/core/src/jsTest/kotlin/css/AnimationTests.kt b/html/core/src/jsTest/kotlin/css/AnimationTests.kt
index eae861b3e0..bbdb52b914 100644
--- a/html/core/src/jsTest/kotlin/css/AnimationTests.kt
+++ b/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"
+ )
+ }
}
diff --git a/html/core/src/jsTest/kotlin/css/StyleSheetTests.kt b/html/core/src/jsTest/kotlin/css/StyleSheetTests.kt
index ac0278b48e..ed5ccab253 100644
--- a/html/core/src/jsTest/kotlin/css/StyleSheetTests.kt
+++ b/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"
+ )
+ }
+
}
\ No newline at end of file