All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.Dialog.kt Maven / Gradle / Ivy

Go to download

Unstyled, fully accesible Compose Multiplatform components that you can customize to your heart's desire.

There is a newer version: 1.19.1
Show newest version
package com.composables.core

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.semantics.dialog
import androidx.compose.ui.semantics.semantics

public data class DialogProperties(val dismissOnBackPress: Boolean = true, val dismissOnClickOutside: Boolean = true)

@Stable
public class DialogState(visible: Boolean = false) {
    public var visible: Boolean by mutableStateOf(visible)
}

@Stable
public class DialogScope internal constructor(state: DialogState) {
    internal var dialogState by mutableStateOf(state)
    internal val visibleState = MutableTransitionState(false)
}

private val DialogStateSaver = run {
    mapSaver(
        save = {
            mapOf("visible" to it.visible)
        },
        restore = {
            DialogState(it["visible"] as Boolean)
        }
    )
}

@Composable
public fun rememberDialogState(visible: Boolean = false): DialogState {
    return rememberSaveable(saver = DialogStateSaver) { DialogState(visible) }
}

@Composable
public fun Dialog(
    state: DialogState = rememberDialogState(),
    properties: DialogProperties = DialogProperties(),
    content: @Composable() (DialogScope.() -> Unit)
) {
    val scope = remember { DialogScope(state) }
    scope.visibleState.targetState = state.visible

    LaunchedEffect(scope.visibleState.currentState) {
        if (scope.visibleState.isIdle && scope.visibleState.currentState.not()) {
            scope.dialogState.visible = false
        }
    }

    if (scope.visibleState.currentState || scope.visibleState.targetState || scope.visibleState.isIdle.not()) {
        Modal {
            if (properties.dismissOnBackPress) {
                KeyDownHandler { event ->
                    return@KeyDownHandler when (event.key) {
                        Key.Back, Key.Escape -> {
                            scope.dialogState.visible = false
                            true
                        }

                        else -> false
                    }
                }
            }
            Box(
                modifier = Modifier.fillMaxSize()
                    .let {
                        if (properties.dismissOnClickOutside) {
                            it.pointerInput(Unit) { detectTapGestures { scope.dialogState.visible = false } }
                        } else it
                    },
                contentAlignment = Alignment.Center
            ) {
                scope.content()
            }
        }
    }
}

@Composable
public fun DialogScope.DialogPanel(
    modifier: Modifier = Modifier,
    enter: EnterTransition = AppearInstantly,
    exit: ExitTransition = DisappearInstantly,
    content: @Composable () -> Unit
) {
    AnimatedVisibility(
        visibleState = visibleState,
        enter = enter,
        exit = exit,
    ) {
        Box(modifier.semantics { dialog() }
            .pointerInput(Unit) { detectTapGestures { } }) {
            content()
        }
    }
}

@Composable
public fun DialogScope.Scrim(
    modifier: Modifier = Modifier,
    scrimColor: Color = Color.Black.copy(0.6f),
    enter: EnterTransition = AppearInstantly,
    exit: ExitTransition = DisappearInstantly,
) {
    AnimatedVisibility(
        visibleState = visibleState,
        enter = enter,
        exit = exit
    ) {
        Box(Modifier.fillMaxSize().focusable(false).background(scrimColor).then(modifier))
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy