Hi could someone help me how use SVGs with Kotlin React.
I'd like to achieve similar thing as with following example using react and javascript:
const MyComponent = ({radius, color}) => (
<svg width={radius * 2} height={radius * 2}>
<circle cx={radius} cy={radius} r={radius} fill= {color}/>
</svg>
)
Unfortunately I haven't been able to find by myself any example or sufficient documentation.
Using Kotlin i don't know what language features I could use to achieve the same. Could anyone help me to fill following snippet using Kotlin?
fun RBuilder.MyComponent(radius: Int, color: String) {
svg {
...
}
}
Thanks a lot.
As of this writing Kotlin does not appear to have a library for svg similiar to kotlinx.html. That being said, it is possible to create arbitrary xml in kotlin-react by reverse engineering the way kotlin-react and kotlinx.html interact. Or just use base react functions.
Method 1: Using kotlinx.html
import kotlinx.html.HTMLTag
import react.*
import react.dom.*
// a custom tag builder, reuses the tag(...) function from kotlin-react and HTMLTag from kotlinx.html
inline fun RBuilder.custom(tagName: String, block: RDOMBuilder<HTMLTag>.() -> Unit): ReactElement = tag(block) {
HTMLTag(tagName, it, mapOf(), null, true, false) // I dont know yet what the last 3 params mean... to lazy to look it up
}
// example use
inline fun RBuilder.mySVG(animTime: Double) {
svg() {
attrs["width"] = "800"
attrs["height"] = "600"
attrs["viewBox"] = "0 0 800 600"
custom("circle") {
attrs["cx"] = 150
attrs["cy"] = 150
attrs["r"] = 50.0 + sin(animTime) * 50.0
attrs["style"] = object {
val stroke = "black"
val fill = "red"
}
}
}
}
Method 2: Using child
and createElement
from react.dom
open class CircleProps (
val cx: Int,
val cy: Int,
val r: Int,
val stroke: String,
val fill: String,
val strokeWidth: Int
): RProps {
}
inline fun RBuilder.mySVG2(animTime: Double) {
svg() {
attrs["width"] = "800"
attrs["height"] = "600"
attrs["viewBox"] = "0 0 800 600"
child(createElement("circle", CircleProps(150,150, 50.0 + sin(animTime) * 50.0, "black", "blue", 3)))
}
I'm not sure how to write SVG inline in the Kotlin/React file, however, JetBrain's own example shows how to load an external SVG file in Kotlin-React.
@JsModule("src/logo/react.svg")
external val reactLogo: dynamic
@JsModule("src/logo/kotlin.svg")
external val kotlinLogo: dynamic
fun RBuilder.logo(height: Int = 100) {
div("Logo") {
attrs.jsStyle.height = height
img(alt = "React logo.logo", src = reactLogo, classes = "Logo-react") {}
img(alt = "Kotlin logo.logo", src = kotlinLogo, classes = "Logo-kotlin") {}
}
}
If you do work out how to do it inline, please post that solution back here for the community, thanks.
All SVG components already declared in react.dom.svg.ReactSVG
object
circle
missed right now - I will add it (fast WA in issue)
path
example
You can also inline SVG files' contents directly into HTML.
Add implementation(devNpm("@svgr/webpack", "6.2.1"))
into your dependencies { ... }
inside build.gradle[.kts].
Configure webpack to make reading and minifying of SVG files possible - create any JS file inside the webpack.config.d folder in your project's root with the following contents:
config.module.rules.push({
test: /\.svg$/,
use: ["@svgr/webpack"]
});
external interface SvgFile {
@JsName("default")
operator fun invoke(): ReactNode
}
@JsModule("./icons/arrow-right-thin.svg")
@JsNonModule
private external val arrowRightThin: SvgFile
and place it where needed in the component:
val example = fc<Props> {
+arrowRightThin()
}
Tested with:
Update (May 14, 2022): I think the interface described in #3 above should be changed into:
external interface SvgFile {
@JsName("default")
val component: ElementType<PropsWithClassName>
}
operator fun SvgFile.invoke(): ReactNode = component.create()
In this way in addition to the usage described in #4 above it also becomes possible to extend an included SVG by applying CSS classes to it:
val example = fc<Props> {
arrowRightThin.component {
attrs.className = ClassName("some-class")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With