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

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

package io.github.lyxnx.compose.pine

import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import io.github.lyxnx.compose.ui.dropShadow
import io.github.lyxnx.compose.ui.ifTrue

/**
 * Represents an icon position within an icon button
 */
public enum class ButtonIconType {

    /**
     * The icon is before the main button content
     *
     * In LTR layouts this will be on the left; on the right in RTL
     */
    Leading,

    /**
     * The icon is after the main button content
     *
     * In LTR layouts this will be on the right; on the left in RTL
     */
    Trailing
}

/**
 * A Pine Theme button
 *
 * This is the simplest button variant, allowing custom content to be given
 *
 * @param onClick called when the button is clicked. If null, the button will not respond to click events
 * @param colors button colors
 * @param modifier modifier to apply to the button
 * @param contentPadding button content padding
 * @param enabled whether the button is enabled and responds to click events
 * @param interactionSource interaction source used to dispatch interaction events to
 * @param content button content
 */
@Composable
public fun Button(
    onClick: (() -> Unit)?,
    modifier: Modifier = Modifier,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.Padding,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable BoxScope.() -> Unit
) {
    val isFocused by interactionSource.collectIsFocusedAsState()

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

    Box(
        modifier = modifier
            .ifTrue(isFocused) {
                dropShadow(
                    color = colors.focusedShadowColor,
                    shape = PineTheme.shapes.small,
                    spread = 2.dp
                )
            }
            .clip(PineTheme.shapes.small)
            .border(
                width = 1.dp,
                color = borderColor,
                shape = PineTheme.shapes.small
            )
            .background(backgroundColor)
            .clickable(
                interactionSource = interactionSource,
                enabled = onClick != null && enabled,
                onClick = onClick ?: {},
                indication = LocalIndication.current,
                role = Role.Button
            )
            .focusable(
                enabled = enabled,
                interactionSource = interactionSource
            )
            .padding(contentPadding),
        contentAlignment = Alignment.Center
    ) {
        CompositionLocalProvider(LocalContentColor provides contentColor) {
            ProvideTextStyle(
                PineTheme.typography.bodyMedium.copy(
                    fontWeight = FontWeight.SemiBold,
                    color = LocalContentColor.current
                )
            ) {
                content()
            }
        }
    }
}

/**
 * A Pine Theme button that contains only text
 *
 * @param text the button's text
 * @param onClick called when the button is clicked
 * @param modifier modifier to apply to the button
 * @param colors button colors
 * @param contentPadding button content padding
 * @param enabled whether the button is enabled and responds to click events
 * @param interactionSource interaction source used to dispatch interaction events to
 * @param textStyle text style to apply to the button - if null, this will take the value of [LocalTextStyle]
 */
@Composable
public fun Button(
    text: String,
    modifier: Modifier = Modifier,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.Padding,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    textStyle: TextStyle? = null,
    onClick: () -> Unit
) {
    Button(
        onClick = onClick,
        colors = colors,
        modifier = modifier,
        contentPadding = contentPadding,
        enabled = enabled,
        interactionSource = interactionSource
    ) {
        Text(
            text = text,
            style = textStyle ?: LocalTextStyle.current,
            color = LocalContentColor.current
        )
    }
}

/**
 * A Pine Theme button that has text and an icon
 *
 * @param icon icon painter - this will be tinted with the content color given by [colors]
 * @param iconType type of icon - leading or trailing
 * @param text the button's text
 * @param onClick called when the button is clicked
 * @param modifier modifier to apply to the button
 * @param colors button colors
 * @param contentPadding button content padding
 * @param enabled whether the button is enabled and responds to click events
 * @param loading whether the button should show a loading indicator. If the button is loading, it will not respond to click events
 * @param interactionSource interaction source used to dispatch interaction events to
 * @param textStyle text style to apply to the button - if null, this will take the value of [LocalTextStyle]
 */
@Composable
public fun Button(
    icon: Painter,
    iconType: ButtonIconType,
    text: String,
    modifier: Modifier = Modifier,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.Padding,
    iconSize: DpSize = ButtonDefaults.IconSize,
    enabled: Boolean = true,
    loading: Boolean = false,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    textStyle: TextStyle? = null,
    onClick: () -> Unit
) {
    Button(
        icon = {
            Icon(
                painter = icon,
                contentDescription = null,
                modifier = Modifier.size(iconSize)
            )
        },
        iconType = iconType,
        text = text,
        onClick = onClick,
        colors = colors,
        modifier = modifier,
        contentPadding = contentPadding,
        enabled = enabled,
        loading = loading,
        interactionSource = interactionSource,
        textStyle = textStyle
    )
}

/**
 * A Pine Theme button that has text and an icon
 *
 * @param icon icon content
 * @param iconType type of icon - leading or trailing
 * @param text the button's text
 * @param onClick called when the button is clicked
 * @param modifier modifier to apply to the button
 * @param colors button colors
 * @param contentPadding button content padding
 * @param enabled whether the button is enabled and responds to click events
 * @param loading whether the button should show a loading indicator. If the button is loading, it will not respond to click events
 * @param interactionSource interaction source used to dispatch interaction events to
 * @param textStyle text style to apply to the button - if null, this will take the value of [LocalTextStyle]
 */
@Composable
public fun Button(
    icon: @Composable RowScope.() -> Unit,
    iconType: ButtonIconType,
    text: String,
    modifier: Modifier = Modifier,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.Padding,
    enabled: Boolean = true,
    loading: Boolean = false,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    textStyle: TextStyle? = null,
    onClick: () -> Unit
) {
    Button(
        onClick = if (loading) null else onClick,
        colors = colors,
        modifier = modifier,
        contentPadding = contentPadding,
        enabled = enabled,
        interactionSource = interactionSource
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) {
            if (loading) {
                ProgressCircle(
                    modifier = Modifier.size(16.dp),
                    circleWidth = 1.dp,
                    trackColor = colors.loadingTrackColor,
                    barColor = colors.loadingBarColor
                )
            } else if (iconType == ButtonIconType.Leading) {
                icon()
            }

            Text(
                text = text,
                style = textStyle ?: LocalTextStyle.current,
                color = LocalContentColor.current,
                modifier = Modifier.padding(
                    start = if (iconType == ButtonIconType.Leading) 8.dp else 0.dp,
                    end = if (iconType == ButtonIconType.Trailing) 8.dp else 0.dp
                )
            )

            if (!loading && iconType == ButtonIconType.Trailing) {
                icon()
            }
        }
    }
}

/**
 * A Pine Theme button that contains only an icon - essentially a FAB
 *
 * @param icon icon painter - this will be tinted with the content color given by [colors]
 * @param onClick called when the button is clicked
 * @param modifier modifier to apply to the button
 * @param iconSize the size of the icon
 * @param colors button colors
 * @param contentPadding button content padding
 * @param enabled whether the button is enabled and responds to click events
 * @param interactionSource interaction source used to dispatch interaction events to
 */
@Composable
public fun Button(
    icon: Painter,
    modifier: Modifier = Modifier,
    iconSize: DpSize = ButtonDefaults.IconSize,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.IconPadding,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    onClick: () -> Unit
) {
    Button(
        icon = {
            Icon(
                painter = icon,
                contentDescription = null,
                modifier = Modifier.size(iconSize)
            )
        },
        onClick = onClick,
        modifier = modifier,
        colors = colors,
        contentPadding = contentPadding,
        enabled = enabled,
        interactionSource = interactionSource
    )
}

/**
 * A Pine Theme button that contains only an icon - essentially a FAB
 *
 * @param icon icon content
 * @param onClick called when the button is clicked
 * @param modifier modifier to apply to the button
 * @param colors button colors
 * @param contentPadding button content padding
 * @param enabled whether the button is enabled and responds to click events
 * @param interactionSource interaction source used to dispatch interaction events to
 */
@Composable
public fun Button(
    icon: @Composable BoxScope.() -> Unit,
    modifier: Modifier = Modifier,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.IconPadding,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    onClick: () -> Unit
) {
    Button(
        onClick = onClick,
        colors = colors,
        modifier = modifier,
        contentPadding = contentPadding,
        enabled = enabled,
        interactionSource = interactionSource
    ) {
        icon()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy