diff --git a/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt b/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt index 2fb1ee162..108467f6d 100644 --- a/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt +++ b/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt @@ -207,10 +207,79 @@ fun StyleScope.backgroundSize(backgroundSize: BackgroundSize) { backgroundSize(backgroundSize.toString()) } +// See: https://developer.mozilla.org/en-US/docs/Web/CSS/background +sealed class Background private constructor(private val value: String) : StylePropertyValue { + override fun toString(): String = value + + private class Keyword(value: String) : Background(value) + + class Repeatable internal constructor( + val image: BackgroundImage?, + val repeat: BackgroundRepeat?, + val size: BackgroundSize?, + val position: BackgroundPosition?, + val blend: BackgroundBlendMode?, // See StyleScope.background for where this is used + val origin: BackgroundOrigin?, + val clip: BackgroundClip?, + val attachment: BackgroundAttachment?, + ) : Background( + buildList { + image?.let { add(it.toString()) } + repeat?.let { add(it) } + position?.let { add(it.toString()) } + size?.let { + // Size must ALWAYS follow position with a slash + // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background#syntax + if (position == null) add(BackgroundPosition.of(CSSPosition.TopLeft)) + add("/") + add(it.toString()) + } + origin?.let { + add(it) + // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background#values + if (clip == null) add(BackgroundClip.BorderBox.toString()) + } + clip?.let { + // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background#values + if (origin == null) add(BackgroundOrigin.PaddingBox.toString()) + add(it) + } + attachment?.let { add(it) } + }.joinToString(" ") + ) + + companion object { + // Keyword + val None: Background = Keyword("none") + + // Global Keywords + val Inherit: Background = Keyword("inherit") + val Initial: Background = Keyword("initial") + val Revert: Background = Keyword("revert") + val Unset: Background = Keyword("unset") + + fun of( + image: BackgroundImage? = null, + repeat: BackgroundRepeat? = null, + size: BackgroundSize? = null, + position: BackgroundPosition? = null, + blend: BackgroundBlendMode? = null, + origin: BackgroundOrigin? = null, + clip: BackgroundClip? = null, + attachment: BackgroundAttachment? = null, + ): Repeatable = Repeatable(image, repeat, size, position, blend, origin, clip, attachment) + } +} + + // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background // Note: Color is actually a separate property and intentionally not included here. // Note: blend mode *is* specified here but needs to be handled externally, since // (probably for legacy reasons?) the `background` property does not accept it. +@Deprecated( + "Please use `Background.of` instead.", + ReplaceWith("Background.of(image, repeat, size, position, blend, origin, clip, attachment)") +) data class CSSBackground( val image: BackgroundImage? = null, val repeat: BackgroundRepeat? = null, @@ -246,10 +315,40 @@ data class CSSBackground( }.joinToString(" ") } +fun StyleScope.background(background: Background) { + property("background", background) +} + +fun StyleScope.background(vararg backgrounds: Background.Repeatable) { + background(null, *backgrounds) +} + fun StyleScope.background(vararg backgrounds: CSSBackground) { background(null, *backgrounds) } +fun StyleScope.background(color: CSSColorValue?, vararg backgrounds: CSSBackground) { + // CSS order is backwards (IMO). We attempt to fix that in Kobweb. + @Suppress("NAME_SHADOWING") val backgrounds = backgrounds.reversed() + property("background", buildString { + append(backgrounds.joinToString(", ")) + // backgrounds only allow you to specify a single color. If provided, it must be included with + // the final layer. + if (color != null) { + if (this.isNotEmpty()) append(' ') + append(color) + } + }) + val defaultBlendMode = BackgroundBlendMode.Normal + val blendModes = backgrounds + .map { it.blend ?: defaultBlendMode } + // Use toString comparison because otherwise equality checks are against instance + .takeIf { blendModes -> blendModes.any { it.toString() != defaultBlendMode.toString() } } + if (blendModes != null) { + property("background-blend-mode", blendModes.joinToString()) + } +} + /** * A Kotlin-idiomatic API to configure the `background` CSS property. * @@ -263,7 +362,9 @@ fun StyleScope.background(vararg backgrounds: CSSBackground) { * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/background */ -fun StyleScope.background(color: CSSColorValue?, vararg backgrounds: CSSBackground) { +fun StyleScope.background(color: CSSColorValue?, vararg backgrounds: Background.Repeatable) { + if (backgrounds.isEmpty()) return + // CSS order is backwards (IMO). We attempt to fix that in Kobweb. @Suppress("NAME_SHADOWING") val backgrounds = backgrounds.reversed() property("background", buildString { diff --git a/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt b/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt index 51697a034..a7776f42d 100644 --- a/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt +++ b/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt @@ -7,7 +7,11 @@ import com.varabyte.kobweb.compose.ui.Modifier import com.varabyte.kobweb.compose.ui.styleModifier import org.jetbrains.compose.web.css.* -fun Modifier.background(vararg backgrounds: CSSBackground) = styleModifier { +fun Modifier.background(background: Background) = styleModifier { + background(background) +} + +fun Modifier.background(vararg backgrounds: Background.Repeatable) = styleModifier { background(*backgrounds) } @@ -24,6 +28,14 @@ fun Modifier.background(vararg backgrounds: CSSBackground) = styleModifier { * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/background */ +fun Modifier.background(color: CSSColorValue?, vararg backgrounds: Background.Repeatable) = styleModifier { + background(color, *backgrounds) +} + +fun Modifier.background(vararg backgrounds: CSSBackground) = styleModifier { + background(*backgrounds) +} + fun Modifier.background(color: CSSColorValue?, vararg backgrounds: CSSBackground) = styleModifier { background(color, *backgrounds) }