Components
Managing Complexity
Composable components help manage software complexity by allowing developers to break down a complex problem into smaller, more manageable pieces. Other benefits include reusability, testability, and the ability to reason about a system in isolation.
The Component typealias
We rely on a small amount of syntactic sugar defined in kweb.components.Component:
typealias Component = ElementCreator<*>
And then we can use an extension function on this to create a component:
fun Component.simple(
    prompt: String = "Enter Your Name",
    name: KVar<String>
) {
    div {
        h1().text(prompt)
        input(type = text).value = name
    }
    div {
        span().text(name.map { "Hello, $it" })
    }
}
And we use the component like this:
    Kweb(port = 16097) {
        doc.body {
            simple(name = kvar("World"))
        }
    }
Components are configured through the extension function parameters, typically through a mixture of:
| Parameter Type | For | 
|---|---|
| KVals | Values that can change but not be modified by the component | 
| KVars | Values that can change or be modified by the component | 
| ObservableLists | Lists of values that can change or be modified by the component | 
| Double, String, etc | Values that don't change | 
The simplest Component's may have no parameters at all, or just one or two, while the most complex might use a DSL builder.
A more complex example
In this example we create a Component that wraps an <input> element styled using the Bulma CSS framework:
interface BulmaClass {
    val cssClassName: String
}
enum class BulmaColor(override val cssClassName: String) : BulmaClass {
    PRIMARY("is-primary"), LINK("is-link"),
    INFO("is-info"), SUCCESS("is-success"),
    WARNING("is-warning"), DANGER("is-danger")
}
enum class BulmaSize(override val cssClassName: String) : BulmaClass {
    SMALL("is-small"), NORMAL("is-normal"),
    MEDIUM("is-medium"), LARGE("is-large")
}
enum class BulmaStyle(override val cssClassName: String) : BulmaClass {
    ROUNDED("is-rounded"),
}
enum class BulmaState(override val cssClassName: String) : BulmaClass {
    NORMAL("is-normal"), HOVER("is-hovered"),
    FOCUS("is-focused"), LOADING("is-loading"),
}
fun Component.bulmaInput(
    type: InputType,
    value: KVar<String>,
    vararg classes : KVal<out BulmaClass> = arrayOf(),
) {
    input(type = type) { inputElement ->
        var inputClassList: KVal<List<String>> = kval(listOf("input"))
        for (c in classes) {
            inputClassList += c.map { listOf(it.cssClassName) }
        }
        with(inputElement) {
            classes(inputClassList.map { it.joinToString(" ") })
            this.value = value
        }
    }
}
This component can then be used like this:
val bulmaUrl = "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css".json
fun shouldWarn(username : String) =
    if (username.length < 5)
        BulmaColor.WARNING
    else
        BulmaColor.SUCCESS
Kweb(port = 12354) {
    doc.head {
        element(
            "link",
            attributes = mapOf(
                "rel" to "stylesheet".json,
                "href" to bulmaUrl
            )
        )
    }
    doc.body {
        val username = kvar("")
        val color = username.map { shouldWarn(it) }
        bulmaInput(type = text, value = username, color)
    }
}