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

commonMain.io.github.lyxnx.compose.pine.RadioButton.kt Maven / Gradle / Ivy

package io.github.lyxnx.compose.pine

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import io.github.lyxnx.compose.ui.dropShadow
import io.github.lyxnx.compose.ui.ifTrue

/**
 * A Pine Theme radio button
 *
 * A radio button is similar to a checkbox, but from a UX perspective, only 1 radio button within a group can be selected
 * at a time and toggling one will deselect the previously selected one
 *
 * @param selected whether this radio button is selected
 * @param onClick called when the radio button is clicked - if null, this button will not respond to click events
 * @param modifier modifier to apply to the radio button
 * @param enabled whether the button is enabled and responds to click events (providing [onClick] is not null)
 * @param colors radio button colors
 * @param interactionSource interaction source used to dispatch interaction events to
 */
@Composable
public fun RadioButton(
    selected: Boolean,
    onClick: ((Boolean) -> Unit)?,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    colors: RadioButtonColors = RadioButtonDefaults.radioButtonColors(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
    val isFocused by interactionSource.collectIsFocusedAsState()

    val borderColor by colors.borderColor(interactionSource, selected, enabled)
    val backgroundColor by colors.backgroundColor(interactionSource, selected, enabled)
    val indicatorColor by colors.indicatorColor.selectColor(interactionSource, enabled)

    val indicatorSize by animateFloatAsState(
        targetValue = if (selected) 0.4f else 0f,
        animationSpec = tween(durationMillis = 100)
    )

    Canvas(
        modifier = modifier
            .ifTrue(onClick != null) {
                Modifier.minimumInteractiveComponentSize()
            }
            .ifTrue(isFocused) {
                dropShadow(
                    color = colors.focusedShadowColor,
                    shape = CircleShape,
                    spread = 2.dp
                )
            }
            .clip(CircleShape)
            .ifTrue(onClick != null) {
                Modifier.selectable(
                    selected = selected,
                    enabled = enabled,
                    role = Role.RadioButton,
                    interactionSource = interactionSource,
                    indication = LocalIndication.current,
                    onClick = {
                        onClick?.invoke(!selected)
                    }
                )
            }
            .border(width = 1.dp, color = borderColor, shape = CircleShape)
            .background(color = backgroundColor)
            .requiredSize(24.dp),
    ) {
        drawCircle(
            color = indicatorColor,
            radius = (size.minDimension * indicatorSize) / 2
        )
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy