Extending the DSL
De Kotlin DSL is uitbreidbaar, waardoor je DSL-functies voor aangepaste componenten of derde-partij bibliotheken kunt toevoegen. Je kunt samengestelde componenten bouwen die de DSL intern gebruiken.
Componenten toevoegen aan de DSL
Om een component beschikbaar te maken in de DSL, maak je een extensiefunctie op HasComponents die de init hulpfunctie gebruikt.
Basis DSL-functie
Hier is het patroon voor een eenvoudige component. Dit voorbeeld gaat ervan uit dat je een aangepaste Badge component hebt:
import com.webforj.concern.HasComponents
import com.webforj.kotlin.dsl.WebforjDsl
import com.webforj.kotlin.dsl.init
import com.example.component.Badge
fun @WebforjDsl HasComponents.badge(
block: @WebforjDsl Badge.() -> Unit = {}
): Badge {
return init(Badge(), block)
}
De init functie doet drie dingen:
- Voegt de component toe aan de bovenliggende container
- Voert de configuratie-blok uit
- Retourneert de geconfigureerde component
Nu kun je de component gebruiken in DSL-code:
div {
badge {
text = "Nieuw"
variant = Badge.Variant.PRIMARY
}
}
Parameters toevoegen
De meeste DSL-functies accepteren algemene parameters vóór de configuratie-blok:
fun @WebforjDsl HasComponents.badge(
text: String? = null,
variant: Badge.Variant? = null,
block: @WebforjDsl Badge.() -> Unit = {}
): Badge {
val badge = Badge()
text?.let { badge.text = it }
variant?.let { badge.variant = it }
return init(badge, block)
}
Het gebruik wordt beknopter:
div {
badge("Nieuw", Badge.Variant.PRIMARY)
badge("Verkoop") {
styles["font-size"] = "12px"
}
}
Samengestelde componenten maken
Een Composite wikkelt meerdere componenten in een enkele herbruikbare eenheid. De DSL werkt goed voor het definiëren van een samengestelde structuur.
Basis samengestelde
class SearchBox : Composite<Div>() {
val searchField: TextField
val searchButton: Button
init {
boundComponent.apply {
styles["display"] = "flex"
styles["gap"] = "8px"
searchField = textField(placeholder = "Zoeken...") {
styles["flex"] = "1"
}
searchButton = button("Zoeken") {
theme = ButtonTheme.PRIMARY
}
}
}
fun onSearch(handler: (String) -> Unit) {
searchButton.onClick {
handler(searchField.text)
}
searchField.onEnter {
handler(searchField.text)
}
}
}
De samengestelde exposeert componentreferenties voor externe toegang en biedt handige methoden voor veelvoorkomende bewerkingen.
DSL-ondersteuning toevoegen
Maak een DSL-functie zodat de samengestelde kan worden gebruikt als ingebouwde componenten:
fun @WebforjDsl HasComponents.searchBox(
block: @WebforjDsl SearchBox.() -> Unit = {}
): SearchBox {
return init(SearchBox(), block)
}
Nu integreert het natuurlijk:
div {
h1("Productcatalogus")
searchBox {
onSearch { query ->
filterProducts(query)
}
}
// Productlijst...
}
Voorbeeld: Statusindicator
Hier is een compleet voorbeeld van een statusindicator-samengestelde:
class StatusIndicator : Composite<Div>() {
private val dot: Div
private val label: Span
var status: Status = Status.INACTIVE
set(value) {
field = value
updateDisplay()
}
var text: String = ""
set(value) {
field = value
label.text = value
}
init {
boundComponent.apply {
styles["display"] = "flex"
styles["align-items"] = "center"
styles["gap"] = "8px"
dot = div {
styles["width"] = "10px"
styles["height"] = "10px"
styles["border-radius"] = "50%"
styles["background"] = "grijs"
}
label = span()
}
updateDisplay()
}
private fun updateDisplay() {
dot.styles["background"] = when (status) {
Status.ACTIVE -> "#22c55e"
Status.WARNING -> "#f59e0b"
Status.ERROR -> "#ef4444"
Status.INACTIVE -> "#9ca3af"
}
}
enum class Status { ACTIVE, WARNING, ERROR, INACTIVE }
}
// DSL-functie
fun @WebforjDsl HasComponents.statusIndicator(
text: String? = null,
status: StatusIndicator.Status? = null,
block: @WebforjDsl StatusIndicator.() -> Unit = {}
): StatusIndicator {
val indicator = StatusIndicator()
text?.let { indicator.text = it }
status?.let { indicator.status = it }
return init(indicator, block)
}
Gebruik:
div {
statusIndicator("Database", StatusIndicator.Status.ACTIVE)
statusIndicator("Cache", StatusIndicator.Status.WARNING)
statusIndicator("Externe API", StatusIndicator.Status.ERROR)
}