mirror of
https://github.com/InsanusMokrassar/JSUIKitKBindings.git
synced 2024-12-22 08:37:24 +00:00
total rework of dialogs
This commit is contained in:
parent
0e1be72219
commit
46a897e629
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## 0.5.0
|
## 0.5.0
|
||||||
|
|
||||||
|
* Fully rework `Dialog` elements
|
||||||
|
|
||||||
## 0.4.3
|
## 0.4.3
|
||||||
|
|
||||||
* Add opportunity to now show dialog automatically
|
* Add opportunity to now show dialog automatically
|
||||||
|
@ -1,33 +1,30 @@
|
|||||||
package dev.inmo.jsuikit.elements
|
package dev.inmo.jsuikit.elements
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Composition
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.DisposableEffectResult
|
import androidx.compose.runtime.DisposableEffectResult
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
import androidx.compose.runtime.currentRecomposeScope
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import dev.inmo.jsuikit.modifiers.*
|
import dev.inmo.jsuikit.modifiers.*
|
||||||
import org.jetbrains.compose.web.dom.*
|
import org.jetbrains.compose.web.dom.*
|
||||||
|
import org.jetbrains.compose.web.renderComposableInBody
|
||||||
import org.w3c.dom.HTMLDivElement
|
import org.w3c.dom.HTMLDivElement
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.MutationObserver
|
||||||
|
import org.w3c.dom.MutationObserverInit
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextUInt
|
import kotlin.random.nextUInt
|
||||||
|
|
||||||
private class DialogDisposableEffectResult(
|
|
||||||
private val element: HTMLElement,
|
|
||||||
private val onDispose: (() -> Unit)?,
|
|
||||||
private val onDisposed: (() -> Unit)?
|
|
||||||
) : DisposableEffectResult {
|
|
||||||
override fun dispose() {
|
|
||||||
onDispose?.invoke()
|
|
||||||
UIKit.modal("#${element.id}") ?.hide()
|
|
||||||
onDisposed?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Dialog(
|
fun Dialog(
|
||||||
vararg modifiers: UIKitModifier,
|
vararg modifiers: UIKitModifier,
|
||||||
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
|
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
|
||||||
onHide: (() -> Unit)? = null,
|
onHidden: ((HTMLDivElement) -> Unit)? = null,
|
||||||
onHidden: (() -> Unit)? = null,
|
onShown: ((HTMLDivElement) -> Unit)? = null,
|
||||||
dialogAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
dialogAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
headerAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
headerAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
headerBuilder: ContentBuilder<HTMLDivElement>? = null,
|
headerBuilder: ContentBuilder<HTMLDivElement>? = null,
|
||||||
@ -37,73 +34,106 @@ fun Dialog(
|
|||||||
footerBuilder: ContentBuilder<HTMLDivElement>? = null,
|
footerBuilder: ContentBuilder<HTMLDivElement>? = null,
|
||||||
bodyAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
bodyAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
autoShow: Boolean = true,
|
autoShow: Boolean = true,
|
||||||
|
removeOnHide: Boolean = true,
|
||||||
bodyBuilder: ContentBuilder<HTMLDivElement>
|
bodyBuilder: ContentBuilder<HTMLDivElement>
|
||||||
) {
|
) {
|
||||||
Div(
|
val drawDiv = remember { mutableStateOf(true) }
|
||||||
{
|
val composition = renderComposableInBody {
|
||||||
if (modifiers.none { it is UIKitModal.WithCustomAttributes }) {
|
|
||||||
include(UIKitModal)
|
|
||||||
}
|
|
||||||
id("dialog${Random.nextUInt()}")
|
|
||||||
include(*modifiers)
|
|
||||||
attributesCustomizer()
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Div(
|
Div(
|
||||||
{
|
{
|
||||||
include(UIKitModal.Dialog)
|
if (modifiers.none { it is UIKitModal.WithCustomAttributes }) {
|
||||||
dialogAttrsBuilder ?.let { it() } ?: include(UIKitMargin.Auto.Vertical)
|
include(UIKitModal)
|
||||||
|
}
|
||||||
|
id("dialog${Random.nextUInt()}")
|
||||||
|
include(*modifiers)
|
||||||
|
|
||||||
|
ref { htmlElement ->
|
||||||
|
inline fun isShown() = htmlElement.classList.contains(UIKitUtility.Open.classes.first())
|
||||||
|
|
||||||
|
var latestStateIsShown = isShown()
|
||||||
|
|
||||||
|
val observer = MutationObserver { _, _ ->
|
||||||
|
val currentStateIsShown = isShown()
|
||||||
|
|
||||||
|
when (currentStateIsShown) {
|
||||||
|
latestStateIsShown -> return@MutationObserver
|
||||||
|
true -> onShown ?.invoke(htmlElement)
|
||||||
|
false -> onHidden ?.invoke(htmlElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
latestStateIsShown = currentStateIsShown
|
||||||
|
|
||||||
|
if (removeOnHide && !currentStateIsShown) {
|
||||||
|
htmlElement.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.observe(htmlElement, MutationObserverInit(attributes = true, attributeFilter = arrayOf("class")))
|
||||||
|
|
||||||
|
if (autoShow) {
|
||||||
|
UIKit.modal("#${htmlElement.id}") ?.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
observer.disconnect()
|
||||||
|
drawDiv.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributesCustomizer()
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
headerBuilder ?.let {
|
|
||||||
Div(
|
|
||||||
{
|
|
||||||
include(UIKitModal.Header)
|
|
||||||
headerAttrsBuilder ?.let { it() }
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
it()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
afterHeaderBuilder ?.let { it() }
|
|
||||||
Div(
|
Div(
|
||||||
{
|
{
|
||||||
include(UIKitModal.Body)
|
include(UIKitModal.Dialog)
|
||||||
bodyAttrsBuilder ?.let { it() }
|
dialogAttrsBuilder ?.let { it() } ?: include(UIKitMargin.Auto.Vertical)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
bodyBuilder()
|
headerBuilder ?.let {
|
||||||
}
|
Div(
|
||||||
beforeFooterBuilder ?.let { it() }
|
{
|
||||||
footerBuilder ?.let {
|
include(UIKitModal.Header)
|
||||||
|
headerAttrsBuilder ?.let { it() }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
it()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
afterHeaderBuilder ?.let { it() }
|
||||||
Div(
|
Div(
|
||||||
{
|
{
|
||||||
include(UIKitModal.Footer)
|
include(UIKitModal.Body)
|
||||||
footerAttrsBuilder ?.let { it() } ?: include(UIKitText.Alignment.Horizontal.Right)
|
bodyAttrsBuilder ?.let { it() }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
it()
|
bodyBuilder()
|
||||||
|
}
|
||||||
|
beforeFooterBuilder ?.let { it() }
|
||||||
|
footerBuilder ?.let {
|
||||||
|
Div(
|
||||||
|
{
|
||||||
|
include(UIKitModal.Footer)
|
||||||
|
footerAttrsBuilder ?.let { it() } ?: include(UIKitText.Alignment.Horizontal.Right)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
it()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DisposableRefEffect {
|
if (drawDiv.value) {
|
||||||
DialogDisposableEffectResult(it, onHide, onHidden)
|
Div({
|
||||||
}
|
hidden()
|
||||||
|
ref {
|
||||||
DomSideEffect { htmlElement ->
|
onDispose {
|
||||||
var wrapper: (Event) -> Unit = {}
|
composition.dispose()
|
||||||
wrapper = { it: Event ->
|
}
|
||||||
htmlElement.removeEventListener("hidden", wrapper)
|
|
||||||
htmlElement.remove()
|
|
||||||
onHidden ?.invoke()
|
|
||||||
}
|
}
|
||||||
htmlElement.addEventListener("hidden", wrapper)
|
})
|
||||||
|
} else {
|
||||||
if (autoShow) {
|
runCatching { composition.dispose() }
|
||||||
UIKit.modal("#${htmlElement.id}") ?.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,31 +141,39 @@ fun Dialog(
|
|||||||
fun Dialog(
|
fun Dialog(
|
||||||
title: String,
|
title: String,
|
||||||
vararg modifiers: UIKitModifier,
|
vararg modifiers: UIKitModifier,
|
||||||
hide: (() -> Unit)? = null,
|
|
||||||
hidden: (() -> Unit)? = null,
|
|
||||||
footerBuilder: (@Composable () -> Unit)? = null,
|
|
||||||
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
|
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
|
||||||
|
onHidden: ((HTMLDivElement) -> Unit)? = null,
|
||||||
|
onShown: ((HTMLDivElement) -> Unit)? = null,
|
||||||
|
dialogAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
|
headerAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
|
headerBuilder: ContentBuilder<HTMLDivElement>? = null,
|
||||||
|
afterHeaderBuilder: ContentBuilder<HTMLDivElement>? = null,
|
||||||
|
beforeFooterBuilder: ContentBuilder<HTMLDivElement>? = null,
|
||||||
|
footerAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
|
footerBuilder: ContentBuilder<HTMLDivElement>? = null,
|
||||||
|
bodyAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
|
||||||
autoShow: Boolean = true,
|
autoShow: Boolean = true,
|
||||||
bodyBuilder: @Composable () -> Unit
|
removeOnHide: Boolean = true,
|
||||||
|
bodyBuilder: ContentBuilder<HTMLDivElement>
|
||||||
) = Dialog(
|
) = Dialog(
|
||||||
modifiers = modifiers,
|
modifiers = modifiers,
|
||||||
|
attributesCustomizer,
|
||||||
|
onHidden,
|
||||||
|
onShown,
|
||||||
|
dialogAttrsBuilder,
|
||||||
|
headerAttrsBuilder,
|
||||||
headerBuilder = {
|
headerBuilder = {
|
||||||
H2({ include(UIKitModal.Title) }) {
|
H2({ include(UIKitModal.Title) }) {
|
||||||
Text(title)
|
Text(title)
|
||||||
}
|
}
|
||||||
|
headerBuilder ?.invoke(this)
|
||||||
},
|
},
|
||||||
onHide = hide,
|
afterHeaderBuilder,
|
||||||
onHidden = hidden,
|
beforeFooterBuilder,
|
||||||
footerBuilder = footerBuilder ?.let { _ ->
|
footerAttrsBuilder,
|
||||||
{
|
footerBuilder,
|
||||||
footerBuilder()
|
bodyAttrsBuilder,
|
||||||
}
|
autoShow,
|
||||||
},
|
removeOnHide,
|
||||||
attributesCustomizer = {
|
bodyBuilder
|
||||||
attributesCustomizer()
|
|
||||||
},
|
|
||||||
autoShow = autoShow,
|
|
||||||
bodyBuilder = {
|
|
||||||
bodyBuilder()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user