package eu.kanade.presentation.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.ColorScheme import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import eu.kanade.presentation.util.minimumTouchTargetSize import kotlin.math.ln /** * Surface with additional onLongClick functionality. * * @see androidx.compose.material3.Surface */ @Composable @NonRestartableComposable fun Surface( onClick: () -> Unit, modifier: Modifier = Modifier, onLongClick: (() -> Unit)? = null, enabled: Boolean = true, shape: Shape = RectangleShape, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable () -> Unit, ) { val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation, ) { Box( modifier = modifier .minimumTouchTargetSize() .surface( shape = shape, backgroundColor = surfaceColorAtElevation( color = color, elevation = absoluteElevation, ), border = border, shadowElevation = shadowElevation, ) .combinedClickable( interactionSource = interactionSource, indication = rememberRipple(), enabled = enabled, role = Role.Button, onLongClick = onLongClick, onClick = onClick, ), propagateMinConstraints = true, ) { content() } } } private fun Modifier.surface( shape: Shape, backgroundColor: Color, border: BorderStroke?, shadowElevation: Dp, ) = this .shadow(shadowElevation, shape, clip = false) .then(if (border != null) Modifier.border(border, shape) else Modifier) .background(color = backgroundColor, shape = shape) .clip(shape) @Composable @ReadOnlyComposable private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color { return if (color == MaterialTheme.colorScheme.surface) { MaterialTheme.colorScheme.surfaceColorAtElevation(elevation) } else { color } } private fun ColorScheme.surfaceColorAtElevation( elevation: Dp, ): Color { if (elevation == 0.dp) return surface val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f return surfaceTint.copy(alpha = alpha).compositeOver(surface) }