diff --git a/CHANGELOG.md b/CHANGELOG.md index d6dde8a..c1b2c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.0.35 +* Add `UIKitModal` +* Improve work with dialogs + ## 0.0.34 * Add `Alert` diff --git a/src/jsMain/kotlin/dev/inmo/jsuikit/elements/Dialog.kt b/src/jsMain/kotlin/dev/inmo/jsuikit/elements/Dialog.kt index 39680b5..96428eb 100644 --- a/src/jsMain/kotlin/dev/inmo/jsuikit/elements/Dialog.kt +++ b/src/jsMain/kotlin/dev/inmo/jsuikit/elements/Dialog.kt @@ -2,8 +2,7 @@ package dev.inmo.jsuikit.elements import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffectResult -import dev.inmo.jsuikit.modifiers.UIKitModifier -import dev.inmo.jsuikit.modifiers.include +import dev.inmo.jsuikit.modifiers.* import org.jetbrains.compose.web.dom.* import org.jetbrains.compose.web.dom.Text import org.w3c.dom.* @@ -25,42 +24,48 @@ private class DialogDisposableEffectResult( @Composable fun Dialog( - title: String? = null, vararg modifiers: UIKitModifier, - hide: (() -> Unit)? = null, - hidden: (() -> Unit)? = null, - footerBuilder: (@Composable () -> Unit)? = null, attributesCustomizer: AttrBuilderContext = {}, - bodyBuilder: @Composable () -> Unit + onHide: (() -> Unit)? = null, + onHidden: (() -> Unit)? = null, + dialogAttrsBuilder: AttrBuilderContext? = null, + headerAttrsBuilder: AttrBuilderContext? = null, + headerBuilder: ContentBuilder? = null, + footerAttrsBuilder: AttrBuilderContext? = null, + footerBuilder: ContentBuilder? = null, + bodyAttrsBuilder: AttrBuilderContext? = null, + bodyBuilder: ContentBuilder ) { Div( { - attr("uk-modal", "") - classes("uk-flex-top", "uk-modal") + if (modifiers.none { it is UIKitModal.WithCustomAttributes }) { + include(UIKitModal) + } id("dialog${Random.nextUInt()}") - include(*modifiers) + include(*modifiers, UIKitFlex.Alignment.Vertical.Top) attributesCustomizer() } ) { Div( { - classes("uk-modal-dialog", "uk-margin-auto-vertical") + include(UIKitModal.Dialog) + dialogAttrsBuilder ?.let { it() } ?: include(UIKitMargin.Auto.Vertical) } ) { - title ?.let { + headerBuilder ?.let { Div( { - classes("uk-modal-header") + include(UIKitModal.Header) + headerAttrsBuilder ?.let { it() } } ) { - H2({ classes("uk-modal-title") }) { - Text(title) - } + it() } } Div( { - classes("uk-modal-body") + include(UIKitModal.Body) + bodyAttrsBuilder ?.let { it() } } ) { bodyBuilder() @@ -68,16 +73,17 @@ fun Dialog( footerBuilder ?.let { Div( { - classes("uk-modal-footer", "uk-text-right") + include(UIKitModal.Footer) + footerAttrsBuilder ?.let { it() } ?: include(UIKitText.Alignment.Horizontal.Right) } ) { - footerBuilder() + it() } } } DisposableRefEffect { - DialogDisposableEffectResult(it, hide, hidden) + DialogDisposableEffectResult(it, onHide, onHidden) } DomSideEffect { htmlElement -> @@ -85,13 +91,44 @@ fun Dialog( wrapper = { it: Event -> htmlElement.removeEventListener("hidden", wrapper) htmlElement.remove() - hidden ?.invoke() + onHidden ?.invoke() } htmlElement.addEventListener("hidden", wrapper) - val dialog = js("UIkit").modal("#${htmlElement.id}") + val dialog = UIKit.modal("#${htmlElement.id}") dialog.show() Unit } } } + +@Composable +fun Dialog( + title: String, + vararg modifiers: UIKitModifier, + hide: (() -> Unit)? = null, + hidden: (() -> Unit)? = null, + footerBuilder: (@Composable () -> Unit)? = null, + attributesCustomizer: AttrBuilderContext = {}, + bodyBuilder: @Composable () -> Unit +) = Dialog( + modifiers = modifiers, + headerBuilder = { + H2({ include(UIKitModal.Title) }) { + Text(title) + } + }, + onHide = hide, + onHidden = hidden, + footerBuilder = footerBuilder ?.let { _ -> + { + footerBuilder() + } + }, + attributesCustomizer = { + attributesCustomizer() + }, + bodyBuilder = { + bodyBuilder() + } +) diff --git a/src/jsMain/kotlin/dev/inmo/jsuikit/modifiers/UIKitModal.kt b/src/jsMain/kotlin/dev/inmo/jsuikit/modifiers/UIKitModal.kt new file mode 100644 index 0000000..67ff13e --- /dev/null +++ b/src/jsMain/kotlin/dev/inmo/jsuikit/modifiers/UIKitModal.kt @@ -0,0 +1,58 @@ +package dev.inmo.jsuikit.modifiers + +import dev.inmo.jsuikit.utils.buildAttribute + +sealed class UIKitModal( + classname: String, + override val otherAttrs: Map = emptyMap() +) : UIKitModifier { + override val classes: Array = arrayOf(classname) + + object Dialog : UIKitModal("uk-modal-dialog") + object Title : UIKitModal("uk-modal-title") + + object Header : UIKitModal("uk-modal-header") + object Body : UIKitModal("uk-modal-body") + object Footer : UIKitModal("uk-modal-footer") + + object Container : UIKitModal("uk-modal-container") + object Full : UIKitModal("uk-modal-full") + + object Page : UIKitModal("uk-modal-page") + + sealed class Close(classname: String) : UIKitModal(classname) { + object Default : Close("uk-modal-close-default") + object Outside : Close("uk-modal-close-outside") + object Full : Close("uk-modal-close-full") + + companion object : Close("uk-modal-close") + } + + internal class WithCustomAttributes( + otherAttrs: Map = emptyMap() + ) : UIKitModal("uk-modal", otherAttrs) + + companion object : UIKitModal("uk-modal", mapOf("uk-modal" to "")) { + operator fun invoke( + escClose: Boolean? = null, + backgroundClose: Boolean? = null, + stack: Boolean? = null, + container: Boolean? = null, + classPage: String? = null, + classPanel: String? = null, + closingSelection: String? = null, + ): UIKitModal = WithCustomAttributes( + mapOf( + buildAttribute("uk-modal") { + "esc-close" to escClose + "bg-close" to backgroundClose + "stack" to stack + "container" to container + "cls-page" to classPage + "cls-panel" to classPanel + "sel-close" to closingSelection + } + ) + ) + } +}