Extending the DSL
Die Kotlin DSL ist erweiterbar und ermöglicht die Hinzufügung von DSL-Funktionen für benutzerdefinierte Komponenten oder Bibliotheken von Drittanbietern. Sie können zusammengesetzte Komponenten erstellen, die die DSL intern verwenden.
Komponenten zur DSL hinzufügen
Um eine beliebige Komponente in der DSL verfügbar zu machen, erstellen Sie eine Erweiterungsfunktion auf HasComponents, die die Hilfsfunktion init verwendet.
Grundlegende DSL-Funktion
Hier ist das Muster für eine einfache Komponente. Dieses Beispiel geht davon aus, dass Sie eine benutzerdefinierte Badge-Komponente haben:
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)
}
Die Funktion init erledigt drei Dinge:
- Fügt die Komponente dem übergeordneten Container hinzu
- Führt den Konfigurationsblock aus
- Gibt die konfigurierte Komponente zurück
Jetzt können Sie die Komponente im DSL-Code verwenden:
div {
badge {
text = "Neu"
variant = Badge.Variant.PRIMARY
}
}
Parameter hinzufügen
Die meisten DSL-Funktionen akzeptieren gängige Parameter vor dem Konfigurationsblock:
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)
}
Die Verwendung wird prägnanter:
div {
badge("Neu", Badge.Variant.PRIMARY)
badge("Verkauf") {
styles["font-size"] = "12px"
}
}
Zusammengesetzte Komponenten erstellen
Ein Composite fasst mehrere Komponenten zu einer einzigen wiederverwendbaren Einheit zusammen. Die DSL eignet sich gut zur Definition der zusammengesetzten Struktur.
Grundlegende Zusammenstellung
class SearchBox : Composite<Div>() {
val searchField: TextField
val searchButton: Button
init {
boundComponent.apply {
styles["display"] = "flex"
styles["gap"] = "8px"
searchField = textField(placeholder = "Suchen...") {
styles["flex"] = "1"
}
searchButton = button("Suchen") {
theme = ButtonTheme.PRIMARY
}
}
}
fun onSearch(handler: (String) -> Unit) {
searchButton.onClick {
handler(searchField.text)
}
searchField.onEnter {
handler(searchField.text)
}
}
}
Das Composite exponiert Komponentenreferenzen für den externen Zugriff und bietet Hilfsmethoden für häufige Operationen.
Unterstützung für DSL hinzufügen
Erstellen Sie eine DSL-Funktion, sodass das Composite wie integrierte Komponenten verwendet werden kann:
fun @WebforjDsl HasComponents.searchBox(
block: @WebforjDsl SearchBox.() -> Unit = {}
): SearchBox {
return init(SearchBox(), block)
}
Jetzt integriert es sich natürlich:
div {
h1("Produktkatalog")
searchBox {
onSearch { query ->
filterProducts(query)
}
}
// Produktliste...
}
Beispiel: Statusanzeige
Hier ist ein komplettes Beispiel für ein Composite zur Statusanzeige:
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"] = "grau"
}
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-Funktion
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)
}
Verwendung:
div {
statusIndicator("Datenbank", StatusIndicator.Status.ACTIVE)
statusIndicator("Cache", StatusIndicator.Status.WARNING)
statusIndicator("Externe API", StatusIndicator.Status.ERROR)
}