mirror of
				https://github.com/InsanusMokrassar/JSUIKitKBindings.git
				synced 2025-11-03 21:50:49 +00:00 
			
		
		
		
	@@ -1,5 +1,9 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,4 +9,4 @@ android.enableJetifier=true
 | 
				
			|||||||
# Project data
 | 
					# Project data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
group=dev.inmo
 | 
					group=dev.inmo
 | 
				
			||||||
version=0.4.3
 | 
					version=0.5.0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,33 +1,24 @@
 | 
				
			|||||||
package dev.inmo.jsuikit.elements
 | 
					package dev.inmo.jsuikit.elements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
import androidx.compose.runtime.DisposableEffectResult
 | 
					import androidx.compose.runtime.DisposableEffect
 | 
				
			||||||
 | 
					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.MutationObserver
 | 
				
			||||||
import org.w3c.dom.events.Event
 | 
					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 +28,104 @@ fun Dialog(
 | 
				
			|||||||
    footerBuilder: ContentBuilder<HTMLDivElement>? = null,
 | 
					    footerBuilder: ContentBuilder<HTMLDivElement>? = null,
 | 
				
			||||||
    bodyAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
 | 
					    bodyAttrsBuilder: AttrBuilderContext<HTMLDivElement>? = null,
 | 
				
			||||||
    autoShow: Boolean = true,
 | 
					    autoShow: Boolean = true,
 | 
				
			||||||
    bodyBuilder: ContentBuilder<HTMLDivElement>
 | 
					    removeOnHide: Boolean = true,
 | 
				
			||||||
 | 
					    bodyBuilder: ContentBuilder<HTMLDivElement> = {}
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    Div(
 | 
					    val drawDiv = remember { mutableStateOf(true) }
 | 
				
			||||||
        {
 | 
					    val composition = remember {
 | 
				
			||||||
            if (modifiers.none { it is UIKitModal.WithCustomAttributes }) {
 | 
					        renderComposableInBody {
 | 
				
			||||||
                include(UIKitModal)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            id("dialog${Random.nextUInt()}")
 | 
					 | 
				
			||||||
            include(*modifiers)
 | 
					 | 
				
			||||||
            attributesCustomizer()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        Div(
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                include(UIKitModal.Dialog)
 | 
					 | 
				
			||||||
                dialogAttrsBuilder ?.let { it() } ?: include(UIKitMargin.Auto.Vertical)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            headerBuilder ?.let {
 | 
					 | 
				
			||||||
                Div(
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        include(UIKitModal.Header)
 | 
					 | 
				
			||||||
                        headerAttrsBuilder ?.let { it() }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    it()
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            afterHeaderBuilder ?.let { it() }
 | 
					 | 
				
			||||||
            Div(
 | 
					            Div(
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    include(UIKitModal.Body)
 | 
					                    if (modifiers.none { it is UIKitModal.WithCustomAttributes }) {
 | 
				
			||||||
                    bodyAttrsBuilder ?.let { it() }
 | 
					                        include(UIKitModal)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    id("dialog${Random.nextUInt()}")
 | 
				
			||||||
 | 
					                    include(*modifiers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    attributesCustomizer()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            ) {
 | 
					            ) {
 | 
				
			||||||
                bodyBuilder()
 | 
					                DisposableEffect(true) {
 | 
				
			||||||
            }
 | 
					                    val htmlElement = scopeElement
 | 
				
			||||||
            beforeFooterBuilder ?.let { it() }
 | 
					
 | 
				
			||||||
            footerBuilder ?.let {
 | 
					                    if (autoShow) {
 | 
				
			||||||
 | 
					                        UIKit.modal(htmlElement).show()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (onHidden != null || removeOnHide) {
 | 
				
			||||||
 | 
					                        htmlElement.addEventListener("hidden", {
 | 
				
			||||||
 | 
					                            onHidden ?.invoke(htmlElement)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (removeOnHide) {
 | 
				
			||||||
 | 
					                                htmlElement.remove()
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    onShown ?.let {
 | 
				
			||||||
 | 
					                        htmlElement.addEventListener("shown", {
 | 
				
			||||||
 | 
					                            onShown(htmlElement)
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    onDispose {
 | 
				
			||||||
 | 
					                        drawDiv.value = false
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Div(
 | 
					                Div(
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        include(UIKitModal.Footer)
 | 
					                        include(UIKitModal.Dialog)
 | 
				
			||||||
                        footerAttrsBuilder ?.let { it() } ?: include(UIKitText.Alignment.Horizontal.Right)
 | 
					                        dialogAttrsBuilder ?.let { it() } ?: include(UIKitMargin.Auto.Vertical)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ) {
 | 
					                ) {
 | 
				
			||||||
                    it()
 | 
					                    headerBuilder ?.let {
 | 
				
			||||||
 | 
					                        Div(
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                include(UIKitModal.Header)
 | 
				
			||||||
 | 
					                                headerAttrsBuilder ?.let { it() }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            it()
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    afterHeaderBuilder ?.let { it() }
 | 
				
			||||||
 | 
					                    Div(
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            include(UIKitModal.Body)
 | 
				
			||||||
 | 
					                            bodyAttrsBuilder ?.let { 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 +133,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 = attributesCustomizer,
 | 
				
			||||||
 | 
					    onHidden = onHidden,
 | 
				
			||||||
 | 
					    onShown = onShown,
 | 
				
			||||||
 | 
					    dialogAttrsBuilder = dialogAttrsBuilder,
 | 
				
			||||||
 | 
					    headerAttrsBuilder = headerAttrsBuilder,
 | 
				
			||||||
    headerBuilder = {
 | 
					    headerBuilder = {
 | 
				
			||||||
        H2({ include(UIKitModal.Title) }) {
 | 
					        H2({ include(UIKitModal.Title) }) {
 | 
				
			||||||
            Text(title)
 | 
					            Text(title)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        headerBuilder ?.invoke(this)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    onHide = hide,
 | 
					    afterHeaderBuilder = afterHeaderBuilder,
 | 
				
			||||||
    onHidden = hidden,
 | 
					    beforeFooterBuilder = beforeFooterBuilder,
 | 
				
			||||||
    footerBuilder = footerBuilder ?.let { _ ->
 | 
					    footerAttrsBuilder = footerAttrsBuilder,
 | 
				
			||||||
        {
 | 
					    footerBuilder = footerBuilder,
 | 
				
			||||||
            footerBuilder()
 | 
					    bodyAttrsBuilder = bodyAttrsBuilder,
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    attributesCustomizer = {
 | 
					 | 
				
			||||||
        attributesCustomizer()
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    autoShow = autoShow,
 | 
					    autoShow = autoShow,
 | 
				
			||||||
    bodyBuilder = {
 | 
					    removeOnHide = removeOnHide,
 | 
				
			||||||
        bodyBuilder()
 | 
					    bodyBuilder = bodyBuilder
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import kotlin.js.Json
 | 
				
			|||||||
external interface UIKit {
 | 
					external interface UIKit {
 | 
				
			||||||
    val notification: UIKitNotifications
 | 
					    val notification: UIKitNotifications
 | 
				
			||||||
    val modal: UIKitDialogs
 | 
					    val modal: UIKitDialogs
 | 
				
			||||||
 | 
					    val util: UIKitUtil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun notification(message: String, parameters: Json)
 | 
					    fun notification(message: String, parameters: Json)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								src/jsMain/kotlin/dev/inmo/jsuikit/types/UIKitUtil.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/jsMain/kotlin/dev/inmo/jsuikit/types/UIKitUtil.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package dev.inmo.jsuikit.types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.w3c.dom.HTMLElement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					external interface UIKitUtil {
 | 
				
			||||||
 | 
					    fun on(selector: String, event: String, callback: () -> Unit)
 | 
				
			||||||
 | 
					    fun on(element: HTMLElement, event: String, callback: () -> Unit)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					package dev.inmo.jsuikit.utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.jetbrains.compose.web.dom.AttrBuilderContext
 | 
				
			||||||
 | 
					import org.w3c.dom.Element
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					operator fun <T : Element> AttrBuilderContext<T>.plus(other: AttrBuilderContext<T>): AttrBuilderContext<T> = {
 | 
				
			||||||
 | 
					    this@plus()
 | 
				
			||||||
 | 
					    other()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user