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
)
}
}