diff --git a/NOTICE.txt b/NOTICE.txt index 687ec212..c9d2c2a7 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -61,6 +61,8 @@ app/src/main/res/drawable-night-xxxhdpi/il_tokens_grid_min_width.png app/src/prod/res/drawable/ic_launcher_background.xml app/src/prod/res/drawable/ic_launcher_foreground.xml +core/src/main/res/drawable/loading_indicator_circular.xml + docs/images/favicon-16x16.png docs/images/orange-logo.svg diff --git a/core/src/main/java/com/orange/ouds/core/component/button/OudsButton.kt b/core/src/main/java/com/orange/ouds/core/component/button/OudsButton.kt index 861252cc..42b252c2 100644 --- a/core/src/main/java/com/orange/ouds/core/component/button/OudsButton.kt +++ b/core/src/main/java/com/orange/ouds/core/component/button/OudsButton.kt @@ -12,90 +12,566 @@ package com.orange.ouds.core.component.button +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.border import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalRippleConfiguration 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.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.orange.ouds.core.R +import com.orange.ouds.core.component.content.OudsComponentContent +import com.orange.ouds.core.component.content.OudsComponentIcon +import com.orange.ouds.core.extensions.InteractionState +import com.orange.ouds.core.extensions.collectInteractionStateAsState import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.core.theme.value import com.orange.ouds.core.utilities.OudsPreview +import com.orange.ouds.foundation.extensions.orElse import com.orange.ouds.foundation.utilities.BasicPreviewParameterProvider import com.orange.ouds.foundation.utilities.UiModePreviews +import com.orange.ouds.theme.outerBorder +import com.orange.ouds.theme.tokens.OudsBorderKeyToken import com.orange.ouds.theme.tokens.OudsColorKeyToken +import com.orange.ouds.theme.tokens.OudsTypographyKeyToken +/** + * An OUDS button which displays only text. + * + * @param text Text displayed in the button. + * @param onClick Callback invoked when the button is clicked. + * @param modifier [Modifier] applied to the button. + * @param enabled Controls the enabled state of the button. When `false`, this button will not be clickable. + * @param loading Controls the loading state of the button. When `true`, this button will display a circular loading indicator. + * @param hierarchy The button hierarchy. + */ @Composable fun OudsButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, - enabled: Boolean = true + enabled: Boolean = true, + loading: Boolean = false, + hierarchy: OudsButton.Hierarchy = OudsButtonDefaults.hierarchy +) { + OudsButton( + nullableIcon = null, + nullableText = text, + onClick = onClick, + modifier = modifier, + enabled = enabled, + loading = loading, + hierarchy = hierarchy + ) +} + +/** + * An OUDS button which displays only an icon. + * + * @param icon Icon displayed in the button. + * @param onClick Callback invoked when the button is clicked. + * @param modifier [Modifier] applied to the button. + * @param enabled Controls the enabled state of the button. When `false`, this button will not be clickable. + * @param loading Controls the loading state of the button. When `true`, this button will display a circular loading indicator. + * @param hierarchy The button hierarchy. + */ +@Composable +fun OudsButton( + icon: OudsButton.Icon, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + loading: Boolean = false, + hierarchy: OudsButton.Hierarchy = OudsButtonDefaults.hierarchy ) { - with(OudsTheme.componentsTokens.button) { + OudsButton( + nullableIcon = icon, + nullableText = null, + onClick = onClick, + modifier = modifier, + enabled = enabled, + loading = loading, + hierarchy = hierarchy + ) +} + +/** + * An OUDS button which displays an icon and text. + * + * @param icon Icon displayed in the button. + * @param text Text displayed in the button. + * @param onClick Callback invoked when the button is clicked. + * @param modifier [Modifier] applied to the button. + * @param enabled Controls the enabled state of the button. When `false`, this button will not be clickable. + * @param loading Controls the loading state of the button. When `true`, this button will display a circular loading indicator. + * @param hierarchy The button hierarchy. + */ +@Composable +fun OudsButton( + icon: OudsButton.Icon, + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + loading: Boolean = false, + hierarchy: OudsButton.Hierarchy = OudsButtonDefaults.hierarchy +) { + OudsButton( + nullableIcon = icon, + nullableText = text, + onClick = onClick, + modifier = modifier, + enabled = enabled, + loading = loading, + hierarchy = hierarchy + ) +} + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +private fun OudsButton( + nullableIcon: OudsButton.Icon?, + nullableText: String?, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + loading: Boolean = false, + hierarchy: OudsButton.Hierarchy = OudsButtonDefaults.hierarchy, + previewState: OudsButton.State? = null +) { + val buttonTokens = OudsTheme.componentsTokens.button + val interactionSource = remember { MutableInteractionSource() } + val interactionState by interactionSource.collectInteractionStateAsState() + val state = previewState.orElse { rememberOudsButtonState(enabled = enabled, loading = loading, interactionState = interactionState) } + val maxHeight = if (nullableIcon != null && nullableText == null) buttonTokens.sizeMaxHeight.dp else Dp.Unspecified + val shape = RoundedCornerShape(buttonTokens.borderRadius.value) + + CompositionLocalProvider(LocalRippleConfiguration provides null) { Button( onClick = onClick, - enabled = enabled, - shape = RoundedCornerShape(cornerRadius.value), - modifier = modifier, - contentPadding = PaddingValues( - vertical = verticalContentPadding.value, - horizontal = horizontalContentPadding.value - ), - interactionSource = remember { MutableInteractionSource() }, - elevation = ButtonDefaults.buttonElevation( - defaultElevation = defaultElevation.value, - pressedElevation = pressedElevation.value, - focusedElevation = focusedElevation.value, - hoveredElevation = hoveredElevation.value, - disabledElevation = disabledElevation.value, - ), - colors = ButtonDefaults.buttonColors( - containerColor = containerColor.value, - contentColor = contentColor.value, - disabledContainerColor = disabledContainerColor.value, - disabledContentColor = disabledContentColor.value, - ) + modifier = modifier + .widthIn(min = buttonTokens.sizeMinWidth.dp) + .heightIn(min = buttonTokens.sizeMinHeight.dp, max = maxHeight) + .buttonBorder(hierarchy = hierarchy, state = state, shape = shape), + enabled = state != OudsButton.State.Disabled && state != OudsButton.State.Loading && state != OudsButton.State.Skeleton, + shape = shape, + colors = buttonColors(hierarchy = hierarchy, buttonState = state), + elevation = null, + contentPadding = contentPadding(icon = nullableIcon, text = nullableText), + interactionSource = interactionSource ) { - Text( - modifier = modifier, - text = text, - style = labelStyle.value, - color = OudsColorKeyToken.Content.OnAction.Primary.Enabled.value + Box(contentAlignment = Alignment.Center) { + val isLoadingIndicatorVisible = state == OudsButton.State.Loading + if (isLoadingIndicatorVisible) { + val infiniteTransition = rememberInfiniteTransition("LoadingIndicatorInfiniteTransition") + val angle by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable(tween(1000, easing = LinearEasing)), + label = "LoadingIndicatorAngleAnimation" + ) + Icon( + modifier = Modifier + .size(buttonTokens.sizeLoader.value) + .graphicsLayer { rotationZ = angle }, + contentDescription = null, + painter = painterResource(id = R.drawable.loading_indicator_circular), + tint = contentColor(hierarchy = hierarchy, state = state) + ) + } + + Row( + modifier = Modifier.alpha(if (isLoadingIndicatorVisible) 0f else 1f), + horizontalArrangement = Arrangement.spacedBy(buttonTokens.spaceColumnGapIcon.value), + verticalAlignment = Alignment.CenterVertically + ) { + if (nullableIcon != null) { + val size = if (nullableText == null) buttonTokens.sizeIconOnly else buttonTokens.sizeIcon + val tint = contentColor(hierarchy = hierarchy, state = state) + nullableIcon.Content( + modifier = Modifier.size(size.value), + extraParameters = OudsButton.Icon.ExtraParameters(tint = tint) + ) + } + if (nullableText != null) { + Text( + modifier = modifier, + text = nullableText, + style = OudsTypographyKeyToken.Label.Strong.Large.value + ) + } + } + } + } + } +} + +@Composable +private fun rememberOudsButtonState( + enabled: Boolean, + loading: Boolean, + interactionState: InteractionState +): OudsButton.State = remember(enabled, loading, interactionState) { + when { + !enabled -> OudsButton.State.Disabled + loading -> OudsButton.State.Loading + interactionState == InteractionState.Hovered -> OudsButton.State.Hovered + interactionState == InteractionState.Pressed -> OudsButton.State.Pressed + interactionState == InteractionState.Focused -> OudsButton.State.Focused + else -> OudsButton.State.Enabled + } +} + +@Composable +private fun Modifier.buttonBorder(hierarchy: OudsButton.Hierarchy, state: OudsButton.State, shape: Shape): Modifier { + val borderWidth = borderWidth(hierarchy = hierarchy, state = state) + val borderColor = borderColor(hierarchy = hierarchy, state = state) + + return if (borderWidth != null && borderColor != null) { + if (state != OudsButton.State.Focused) { + border(width = borderWidth, color = borderColor, shape = shape) + } else { + outerBorder( + width = borderWidth, + color = borderColor, + shape = shape, + insetWidth = OudsBorderKeyToken.Width.Focus.Inset.value, + insetColor = OudsColorKeyToken.Border.Focus.Inset.value ) } + } else { + this + } +} + +@Composable +private fun borderWidth(hierarchy: OudsButton.Hierarchy, state: OudsButton.State): Dp? { + return with(OudsTheme.componentsTokens.button) { + when (hierarchy) { + OudsButton.Hierarchy.Default -> when (state) { + OudsButton.State.Enabled, + OudsButton.State.Disabled -> borderWidthDefault + OudsButton.State.Hovered, + OudsButton.State.Pressed, + OudsButton.State.Loading -> borderWidthDefaultInteraction + OudsButton.State.Focused -> OudsBorderKeyToken.Width.Focus + OudsButton.State.Skeleton -> null + } + OudsButton.Hierarchy.Minimal -> when (state) { + OudsButton.State.Enabled, + OudsButton.State.Disabled -> borderWidthMinimal + OudsButton.State.Hovered, + OudsButton.State.Pressed, + OudsButton.State.Loading -> borderWidthMinimalInteraction + OudsButton.State.Focused -> OudsBorderKeyToken.Width.Focus + OudsButton.State.Skeleton -> null + } + OudsButton.Hierarchy.Strong, + OudsButton.Hierarchy.Negative -> if (state == OudsButton.State.Focused) OudsBorderKeyToken.Width.Focus else null + }?.value } } -@Suppress("PreviewShouldNotBeCalledRecursively") -@UiModePreviews.Button @Composable -private fun PreviewOudsButton(@PreviewParameter(OudsButtonPreviewParameterProvider::class) parameter: OudsButtonPreviewParameter) = OudsPreview { +private fun borderColor(hierarchy: OudsButton.Hierarchy, state: OudsButton.State): Color? { + return with(OudsTheme.componentsTokens.button) { + when (hierarchy) { + OudsButton.Hierarchy.Default -> when (state) { + OudsButton.State.Enabled -> colorBorderDefaultEnabled + OudsButton.State.Hovered -> colorBorderDefaultHover + OudsButton.State.Pressed -> colorBorderDefaultPressed + OudsButton.State.Loading -> colorBorderDefaultLoading + OudsButton.State.Disabled -> colorBorderDefaultDisabled + OudsButton.State.Focused -> OudsColorKeyToken.Border.Focus + OudsButton.State.Skeleton -> null + } + OudsButton.Hierarchy.Minimal -> when (state) { + OudsButton.State.Enabled -> colorBorderMinimalEnabled + OudsButton.State.Hovered -> colorBorderMinimalHover + OudsButton.State.Pressed -> colorBorderMinimalPressed + OudsButton.State.Loading -> colorBorderMinimalLoading + OudsButton.State.Disabled -> colorBorderMinimalDisabled + OudsButton.State.Focused -> OudsColorKeyToken.Border.Focus + OudsButton.State.Skeleton -> null + } + OudsButton.Hierarchy.Strong, + OudsButton.Hierarchy.Negative -> if (state == OudsButton.State.Focused) OudsColorKeyToken.Border.Focus else null + }?.value + } +} + +@Composable +private fun buttonColors(hierarchy: OudsButton.Hierarchy, buttonState: OudsButton.State): ButtonColors { + return ButtonDefaults.buttonColors( + containerColor = containerColor(hierarchy = hierarchy, state = buttonState), + contentColor = contentColor(hierarchy = hierarchy, state = buttonState), + disabledContainerColor = containerColor(hierarchy = hierarchy, state = buttonState), + disabledContentColor = contentColor(hierarchy = hierarchy, state = buttonState) + ) +} + +@Composable +private fun containerColor(hierarchy: OudsButton.Hierarchy, state: OudsButton.State): Color { + return with(OudsTheme.componentsTokens.button) { + when (hierarchy) { + OudsButton.Hierarchy.Default -> when (state) { + OudsButton.State.Enabled -> colorBgDefaultEnabled + OudsButton.State.Focused -> colorBgDefaultFocus + OudsButton.State.Hovered -> colorBgDefaultHover + OudsButton.State.Pressed -> colorBgDefaultPressed + OudsButton.State.Loading -> colorBgDefaultLoading + OudsButton.State.Disabled -> colorBgDefaultDisabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + OudsButton.Hierarchy.Minimal -> when (state) { + OudsButton.State.Enabled -> colorBgMinimalEnabled + OudsButton.State.Focused -> colorBgMinimalFocus + OudsButton.State.Hovered -> colorBgMinimalHover + OudsButton.State.Pressed -> colorBgMinimalPressed + OudsButton.State.Loading -> colorBgMinimalLoading + OudsButton.State.Disabled -> colorBgMinimalDisabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + OudsButton.Hierarchy.Strong -> when (state) { + OudsButton.State.Enabled -> OudsColorKeyToken.Action.Primary.Enabled + OudsButton.State.Focused -> OudsColorKeyToken.Action.Primary.Focus + OudsButton.State.Hovered -> OudsColorKeyToken.Action.Primary.Hover + OudsButton.State.Pressed -> OudsColorKeyToken.Action.Primary.Pressed + OudsButton.State.Loading -> OudsColorKeyToken.Action.Primary.Loading + OudsButton.State.Disabled -> OudsColorKeyToken.Action.Disabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + OudsButton.Hierarchy.Negative -> when (state) { + OudsButton.State.Enabled -> OudsColorKeyToken.Action.Negative.Enabled + OudsButton.State.Focused -> OudsColorKeyToken.Action.Negative.Focus + OudsButton.State.Hovered -> OudsColorKeyToken.Action.Negative.Hover + OudsButton.State.Pressed -> OudsColorKeyToken.Action.Negative.Pressed + OudsButton.State.Loading -> OudsColorKeyToken.Action.Negative.Loading + OudsButton.State.Disabled -> OudsColorKeyToken.Action.Disabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + }.value + } +} + +@Composable +private fun contentColor(hierarchy: OudsButton.Hierarchy, state: OudsButton.State): Color { + return with(OudsTheme.componentsTokens.button) { + when (hierarchy) { + OudsButton.Hierarchy.Default -> when (state) { + OudsButton.State.Enabled -> colorContentDefaultEnabled + OudsButton.State.Focused -> colorContentDefaultFocus + OudsButton.State.Hovered -> colorContentDefaultHover + OudsButton.State.Pressed -> colorContentDefaultPressed + OudsButton.State.Loading -> colorContentDefaultLoading + OudsButton.State.Disabled -> colorContentDefaultDisabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + OudsButton.Hierarchy.Minimal -> when (state) { + OudsButton.State.Enabled -> colorContentMinimalEnabled + OudsButton.State.Focused -> colorContentMinimalFocus + OudsButton.State.Hovered -> colorContentMinimalHover + OudsButton.State.Pressed -> colorContentMinimalPressed + OudsButton.State.Loading -> colorContentMinimalLoading + OudsButton.State.Disabled -> colorContentMinimalDisabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + OudsButton.Hierarchy.Strong -> when (state) { + OudsButton.State.Enabled -> OudsColorKeyToken.Content.OnAction.Primary.Enabled + OudsButton.State.Focused -> OudsColorKeyToken.Content.OnAction.Primary.Focus + OudsButton.State.Hovered -> OudsColorKeyToken.Content.OnAction.Primary.Hover + OudsButton.State.Pressed -> OudsColorKeyToken.Content.OnAction.Primary.Pressed + OudsButton.State.Loading -> OudsColorKeyToken.Content.OnAction.Primary.Loading + OudsButton.State.Disabled -> OudsColorKeyToken.Content.OnAction.Disabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + OudsButton.Hierarchy.Negative -> when (state) { + OudsButton.State.Enabled, + OudsButton.State.Hovered, + OudsButton.State.Pressed, + OudsButton.State.Loading, + OudsButton.State.Focused -> OudsColorKeyToken.Content.OnAction.Negative + OudsButton.State.Disabled -> OudsColorKeyToken.Content.OnAction.Disabled + OudsButton.State.Skeleton -> OudsColorKeyToken.Gradient.Skeleton.StartEnd + } + }.value + } +} + +@Composable +private fun contentPadding(icon: OudsButton.Icon?, text: String?): PaddingValues { + return with(OudsTheme.componentsTokens.button) { + when { + icon != null && text != null -> PaddingValues( + start = spacePaddingInlineIconStart.value, + top = spacePaddingBlock.value, + end = spacePaddingInlineEndIconStart.value, + bottom = spacePaddingBlock.value + ) + icon != null && text == null -> PaddingValues( + horizontal = spaceInsetIconAlone.value, + vertical = spacePaddingBlock.value + ) + else -> PaddingValues( + horizontal = spacePaddingInlineIconNone.value, + vertical = spacePaddingBlock.value + ) + } + } +} + +/** + * Contains the default values used by OUDS buttons. + */ +object OudsButtonDefaults { + + /** + * The default hierarchy. + */ + val hierarchy = OudsButton.Hierarchy.Default +} + +/** + * Contains classes to build an [com.orange.ouds.core.component.button.OudsButton]. + */ +object OudsButton { + + /** + * A button icon in an [OudsButton]. + * It is non-clickable and no content description is needed cause a button label is always present. + */ + class Icon private constructor( + val graphicsObject: Any + ) : OudsComponentIcon(ExtraParameters::class.java, graphicsObject, "") { + + @ConsistentCopyVisibility + data class ExtraParameters internal constructor( + internal val tint: Color + ) : OudsComponentContent.ExtraParameters() + + /** + * Creates an instance of [OudsButton.Icon]. + * + * @param painter Painter of the icon. + */ + constructor(painter: Painter) : this(painter as Any) + + /** + * Creates an instance of [OudsButton.Icon]. + * + * @param imageVector Image vector of the icon. + */ + constructor(imageVector: ImageVector) : this(imageVector as Any) + + /** + * Creates an instance of [OudsButton.Icon]. + * + * @param bitmap Image bitmap of the icon. + */ + constructor(bitmap: ImageBitmap) : this(bitmap as Any) + + override val tint: Color? + @Composable + get() = extraParameters.tint + } + + /** + * Represents the hierarchy of an OUDS button. + */ + enum class Hierarchy { + Default, Strong, Minimal, Negative + } + + internal enum class State { + Enabled, Hovered, Pressed, Loading, Disabled, Focused, Skeleton + } +} + +@UiModePreviews.Default +@Composable +@Suppress("PreviewShouldNotBeCalledRecursively") +private fun PreviewOudsButton(@PreviewParameter(OudsButtonPreviewParameterProvider::class) parameter: OudsButtonPreviewParameter) { PreviewOudsButton(darkThemeEnabled = isSystemInDarkTheme(), parameter = parameter) } @Composable -internal fun PreviewOudsButton(darkThemeEnabled: Boolean, parameter: OudsButtonPreviewParameter) = OudsPreview(darkThemeEnabled) { +internal fun PreviewOudsButton( + darkThemeEnabled: Boolean, + parameter: OudsButtonPreviewParameter +) = OudsPreview(modifier = Modifier.padding(16.dp), darkThemeEnabled = darkThemeEnabled) { with(parameter) { - OudsButton(text = "Text", onClick = {}, enabled = enabled) + val text = if (hasText) hierarchy.name else null + val icon = if (hasIcon) OudsButton.Icon(painter = painterResource(id = android.R.drawable.star_on)) else null + val chunkedStates = parameter.states.chunked(2) + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + chunkedStates.forEach { states -> + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + states.forEach { state -> + OudsButton(nullableIcon = icon, nullableText = text, onClick = {}, hierarchy = hierarchy, previewState = state) + } + } + } + } } } internal data class OudsButtonPreviewParameter( - val enabled: Boolean = true + val hierarchy: OudsButton.Hierarchy, + val hasText: Boolean, + val hasIcon: Boolean, + val states: List = listOf( + OudsButton.State.Enabled, + OudsButton.State.Hovered, + OudsButton.State.Pressed, + OudsButton.State.Loading, + OudsButton.State.Disabled, + OudsButton.State.Focused, +// OudsButton.State.Skeleton + ) ) -internal class OudsButtonPreviewParameterProvider : - BasicPreviewParameterProvider(*previewParameterValues.toTypedArray()) +internal class OudsButtonPreviewParameterProvider : BasicPreviewParameterProvider(*previewParameterValues.toTypedArray()) private val previewParameterValues: List - get() = mutableListOf().apply { - add(OudsButtonPreviewParameter()) - add(OudsButtonPreviewParameter(enabled = false)) + get() = buildList { + OudsButton.Hierarchy.entries.forEach { hierarchy -> + add(OudsButtonPreviewParameter(hierarchy, hasText = true, hasIcon = false)) + add(OudsButtonPreviewParameter(hierarchy, hasText = true, hasIcon = true)) + add(OudsButtonPreviewParameter(hierarchy, hasText = false, hasIcon = true)) + } } - diff --git a/core/src/main/java/com/orange/ouds/core/component/button/OudsIconButton.kt b/core/src/main/java/com/orange/ouds/core/component/button/OudsIconButton.kt new file mode 100644 index 00000000..7da560f7 --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/component/button/OudsIconButton.kt @@ -0,0 +1,33 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.component.button + +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.orange.ouds.core.component.icon.OudsIcon + +@Composable +internal fun OudsIconButton( + onClick: () -> Unit, + graphicsObject: Any, + contentDescription: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + tint: Color = OudsIconButtonDefaults.tint, +) { + IconButton(onClick = onClick, modifier = modifier, enabled = enabled) { + OudsIcon(graphicsObject = graphicsObject, contentDescription = contentDescription, tint = tint, enabled = enabled) + } +} diff --git a/core/src/main/java/com/orange/ouds/core/component/button/OudsIconButtonDefaults.kt b/core/src/main/java/com/orange/ouds/core/component/button/OudsIconButtonDefaults.kt new file mode 100644 index 00000000..375105b4 --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/component/button/OudsIconButtonDefaults.kt @@ -0,0 +1,24 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.component.button + +import androidx.compose.material3.LocalContentColor +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +internal object OudsIconButtonDefaults { + + val tint: Color + @Composable + get() = LocalContentColor.current +} diff --git a/core/src/main/java/com/orange/ouds/core/component/content/OudsComponentContent.kt b/core/src/main/java/com/orange/ouds/core/component/content/OudsComponentContent.kt new file mode 100644 index 00000000..00529d1b --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/component/content/OudsComponentContent.kt @@ -0,0 +1,99 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.component.content + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.Modifier +import com.orange.ouds.foundation.extensions.asOrNull +import com.orange.ouds.foundation.extensions.orElse + + +internal val localExtraParametersByClass = + mutableMapOf, ProvidableCompositionLocal>() + +internal fun getLocalExtraParameters(clazz: Class): ProvidableCompositionLocal where T : OudsComponentContent.ExtraParameters { + return localExtraParametersByClass[clazz]?.asOrNull>().orElse { + staticCompositionLocalOf { error("CompositionLocal LocalExtraParameters for class ${clazz.name} not present") }.also { compositionLocal -> + localExtraParametersByClass[clazz] = compositionLocal + } + } +} + +/** + * The content of a component. + * + * Subclasses of [OudsComponentContent] should be used instead of composable methods when passing parameters to components. + * This prevents using generic composable methods that can encapsulate any kind of views and thus helps developers to follow UI guidelines more easily. + * This also allows to group parameters that are related to the same content inside a component. + * For instance it is possible to create an `Icon` subclass to replace both `icon: @Composable () -> Unit` and `onIconClick: () -> Unit` parameters with a single `icon: Icon` parameter. + * + * @param extraParametersClass The extra parameters class. + * @param T The type of extra parameters. + */ +abstract class OudsComponentContent internal constructor(private val extraParametersClass: Class) where T : OudsComponentContent.ExtraParameters { + + /** + * Extra parameters that can be passed to the `Content` method when other parameters than those provided by the user are needed to layout the component. + */ + abstract class ExtraParameters + + /** + * The extra parameters. + */ + protected val extraParameters: T + @Composable + get() = getLocalExtraParameters(extraParametersClass).current + + /** + * The Jetpack Compose UI for this component content. + * + * Calls `Content(Modifier)` with the default `Modifier`. + */ + @Composable + internal fun Content() = Content(modifier = Modifier) + + /** + * The Jetpack Compose UI for this component content. + * + * Calls `Content(Modifier, T)` with the default `Modifier`. + * + * @param extraParameters The extra parameters for this content. + */ + @Composable + internal fun Content(extraParameters: T) = Content(modifier = Modifier, extraParameters = extraParameters) + + /** + * The Jetpack Compose UI for this component content. + * + * @param modifier The modifier for this content. + * @param extraParameters The extra parameters for this content. + */ + @Composable + internal fun Content(modifier: Modifier, extraParameters: T) { + CompositionLocalProvider(getLocalExtraParameters(extraParametersClass) provides extraParameters) { + Content(modifier = modifier) + } + } + + /** + * The Jetpack Compose UI for this component content. + * Subclasses must implement this method to provide content. + * + * @param modifier The modifier for this content. + */ + @Composable + internal abstract fun Content(modifier: Modifier) +} diff --git a/core/src/main/java/com/orange/ouds/core/component/content/OudsComponentIcon.kt b/core/src/main/java/com/orange/ouds/core/component/content/OudsComponentIcon.kt new file mode 100644 index 00000000..9b005909 --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/component/content/OudsComponentIcon.kt @@ -0,0 +1,91 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.component.content + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import com.orange.ouds.core.component.button.OudsIconButton +import com.orange.ouds.core.component.icon.OudsIcon +import com.orange.ouds.core.component.icon.OudsIconDefaults +import com.orange.ouds.foundation.extensions.orElse + +/** + * An icon in a component. + */ +abstract class OudsComponentIcon protected constructor( + extraParametersClass: Class, + private val graphicsObject: Any, + private val contentDescription: String, + private var enabled: Boolean = true, + private val onClick: (() -> Unit)? = null, +) : OudsComponentContent(extraParametersClass) where T : OudsComponentContent.ExtraParameters { + + val painter: Painter? = graphicsObject as? Painter + val imageVector: ImageVector? = graphicsObject as? ImageVector + val bitmap: ImageBitmap? = graphicsObject as? ImageBitmap + + protected open val tint: Color? + @Composable + get() = null + + protected constructor( + extraParametersClass: Class, + painter: Painter, + contentDescription: String, + enabled: Boolean = true, + onClick: (() -> Unit)? = null, + ) : this(extraParametersClass, painter as Any, contentDescription, enabled, onClick) + + protected constructor( + extraParametersClass: Class, + imageVector: ImageVector, + contentDescription: String, + enabled: Boolean = true, + onClick: (() -> Unit)? = null + ) : this(extraParametersClass, imageVector as Any, contentDescription, enabled, onClick) + + protected constructor( + extraParametersClass: Class, + bitmap: ImageBitmap, + contentDescription: String, + enabled: Boolean = true, + onClick: (() -> Unit)? = null + ) : this(extraParametersClass, bitmap as Any, contentDescription, enabled, onClick) + + @Composable + override fun Content(modifier: Modifier) { + val tint = tint.orElse { OudsIconDefaults.tint } + onClick?.let { onClick -> + OudsIconButton( + onClick = onClick, + graphicsObject = graphicsObject, + contentDescription = contentDescription, + tint = tint, + modifier = modifier, + enabled = enabled + ) + }.orElse { + OudsIcon( + graphicsObject = graphicsObject, + contentDescription = contentDescription, + modifier = modifier, + tint = tint, + enabled = enabled + ) + } + } +} diff --git a/core/src/main/java/com/orange/ouds/core/component/icon/OudsIcon.kt b/core/src/main/java/com/orange/ouds/core/component/icon/OudsIcon.kt new file mode 100644 index 00000000..8c44ef8a --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/component/icon/OudsIcon.kt @@ -0,0 +1,39 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.component.icon + +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import com.orange.ouds.core.extensions.enabled + +@Composable +internal fun OudsIcon( + graphicsObject: Any, + contentDescription: String, + modifier: Modifier = Modifier, + tint: Color = OudsIconDefaults.tint, + enabled: Boolean = true, +) { + val iconTint = tint.enabled(enabled = enabled) + when (graphicsObject) { + is Painter -> Icon(painter = graphicsObject, contentDescription = contentDescription, modifier = modifier, tint = iconTint) + is ImageVector -> Icon(imageVector = graphicsObject, contentDescription = contentDescription, modifier = modifier, tint = iconTint) + is ImageBitmap -> Icon(bitmap = graphicsObject, contentDescription = contentDescription, modifier = modifier, tint = iconTint) + else -> {} + } +} diff --git a/core/src/main/java/com/orange/ouds/core/component/icon/OudsIconDefaults.kt b/core/src/main/java/com/orange/ouds/core/component/icon/OudsIconDefaults.kt new file mode 100644 index 00000000..ef0e493e --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/component/icon/OudsIconDefaults.kt @@ -0,0 +1,24 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.component.icon + +import androidx.compose.material3.LocalContentColor +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +internal object OudsIconDefaults { + + val tint: Color + @Composable + get() = LocalContentColor.current +} diff --git a/core/src/main/java/com/orange/ouds/core/extensions/ColorExt.kt b/core/src/main/java/com/orange/ouds/core/extensions/ColorExt.kt new file mode 100644 index 00000000..d9b5ac02 --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/extensions/ColorExt.kt @@ -0,0 +1,19 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.extensions + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +@Composable +internal fun Color.enabled(enabled: Boolean) = if (enabled) this else copy(alpha = 0.38f) diff --git a/core/src/main/java/com/orange/ouds/core/extensions/InteractionSourceExt.kt b/core/src/main/java/com/orange/ouds/core/extensions/InteractionSourceExt.kt new file mode 100644 index 00000000..14e567b1 --- /dev/null +++ b/core/src/main/java/com/orange/ouds/core/extensions/InteractionSourceExt.kt @@ -0,0 +1,45 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.core.extensions + +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.interaction.collectIsHoveredAsState +import androidx.compose.foundation.interaction.collectIsPressedAsState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember + +internal enum class InteractionState { + None, Focused, Hovered, Pressed +} + +@Composable +internal fun InteractionSource.collectInteractionStateAsState(): State { + val isFocused by collectIsFocusedAsState() + val isHovered by collectIsHoveredAsState() + val isPressed by collectIsPressedAsState() + + return remember { + derivedStateOf { + when { + isFocused -> InteractionState.Focused + isHovered -> InteractionState.Hovered + isPressed -> InteractionState.Pressed + else -> InteractionState.None + } + } + } +} diff --git a/core/src/main/java/com/orange/ouds/core/utilities/OudsPreview.kt b/core/src/main/java/com/orange/ouds/core/utilities/OudsPreview.kt index 2869283b..73acd256 100644 --- a/core/src/main/java/com/orange/ouds/core/utilities/OudsPreview.kt +++ b/core/src/main/java/com/orange/ouds/core/utilities/OudsPreview.kt @@ -12,9 +12,11 @@ package com.orange.ouds.core.utilities +import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.Surface +import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import com.orange.ouds.core.BuildConfig import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.core.theme.value @@ -23,12 +25,21 @@ import com.orange.ouds.theme.tokens.OudsColorKeyToken /** * Configures the Compose OUDS preview environment in Android Studio. * + * @param modifier The modifier for the preview content. * @param darkThemeEnabled Indicates whether the dark theme is enabled or not. * @param content The content of the preview. */ @Composable -fun OudsPreview(darkThemeEnabled: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { +fun OudsPreview(modifier: Modifier = Modifier, darkThemeEnabled: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { OudsTheme(themeContract = BuildConfig.PREVIEW_THEME, darkThemeEnabled) { - Surface(color = OudsColorKeyToken.Background.Primary.value, content = content) // Add a surface to be able to see components + // Add a box to be able to see components + // Use a box instead of a surface to avoid clipping children in cases where something is drawn outside of the component to preview + Box( + modifier = Modifier + .background(OudsColorKeyToken.Background.Primary.value) + .then(modifier) + ) { + content() + } } } diff --git a/core/src/main/res/drawable/loading_indicator_circular.xml b/core/src/main/res/drawable/loading_indicator_circular.xml new file mode 100644 index 00000000..91913144 --- /dev/null +++ b/core/src/main/res/drawable/loading_indicator_circular.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/foundation/src/main/java/com/orange/ouds/foundation/utilities/Preview.kt b/foundation/src/main/java/com/orange/ouds/foundation/utilities/Preview.kt index d81ec731..68304b9f 100644 --- a/foundation/src/main/java/com/orange/ouds/foundation/utilities/Preview.kt +++ b/foundation/src/main/java/com/orange/ouds/foundation/utilities/Preview.kt @@ -46,14 +46,9 @@ annotation class UiModePreviews { companion object { private const val LightName = "Light" private const val DarkName = "Dark" - private const val ButtonWidthDp = 200 } @Preview(name = LightName) @Preview(name = DarkName, uiMode = Configuration.UI_MODE_NIGHT_YES) annotation class Default - - @Preview(name = LightName, widthDp = ButtonWidthDp) - @Preview(name = DarkName, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = ButtonWidthDp) - annotation class Button } diff --git a/theme-contract/src/main/java/com/orange/ouds/theme/OudsBorderModifier.kt b/theme-contract/src/main/java/com/orange/ouds/theme/OudsBorderModifier.kt index dc3851fa..d00cdf23 100644 --- a/theme-contract/src/main/java/com/orange/ouds/theme/OudsBorderModifier.kt +++ b/theme-contract/src/main/java/com/orange/ouds/theme/OudsBorderModifier.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.StampedPathEffectStyle import androidx.compose.ui.graphics.drawOutline +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.unit.Dp @@ -101,25 +102,33 @@ fun Modifier.dottedBorder( /** * Modify element to add an outer border (drawn outside the element) with appearance specified with a [width], a [color] and a [shape]. * - * @param width Thickness of the border in dp - * @param color Color to paint the border with - * @param shape Shape of the border + * @param width The width of the border in dp. Use [Dp.Hairline] for a hairline border. + * @param color The color to paint the border with. + * @param shape The shape of the border. + * @param width The width of the border inset in dp. Use [Dp.Hairline] for a hairline border inset. + * @param color The color to paint the border inset with. */ fun Modifier.outerBorder( width: Dp, color: Color, - shape: Shape = RectangleShape + shape: Shape = RectangleShape, + insetWidth: Dp = Dp.Unspecified, + insetColor: Color = Color.Unspecified ) = drawWithContent { - val outerSize = Size(size.width + width.toPx(), size.height + width.toPx()) - val outline = shape.createOutline(outerSize, layoutDirection, density = this) - val stroke = Stroke(width = width.toPx()) - drawContent() - translate(-width.toPx() / 2f, -width.toPx() / 2f) { - drawOutline( - outline = outline, - style = stroke, - brush = SolidColor(color) - ) + drawOuterBorder(width, color, shape) + drawOuterBorder(insetWidth, insetColor, shape) +} + +private fun DrawScope.drawOuterBorder(width: Dp, color: Color, shape: Shape) { + if (width != Dp.Unspecified) { + val outerSize = Size(size.width + width.toPx(), size.height + width.toPx()) + translate(-width.toPx() / 2f, -width.toPx() / 2f) { + drawOutline( + outline = shape.createOutline(outerSize, layoutDirection, this), + style = Stroke(width.toPx()), + brush = SolidColor(color) + ) + } } } diff --git a/theme-contract/src/main/java/com/orange/ouds/theme/tokens/components/OudsButtonTokens.kt b/theme-contract/src/main/java/com/orange/ouds/theme/tokens/components/OudsButtonTokens.kt index 32ec9cf1..c8a9f6c6 100644 --- a/theme-contract/src/main/java/com/orange/ouds/theme/tokens/components/OudsButtonTokens.kt +++ b/theme-contract/src/main/java/com/orange/ouds/theme/tokens/components/OudsButtonTokens.kt @@ -14,22 +14,64 @@ package com.orange.ouds.theme.tokens.components import com.orange.ouds.theme.tokens.OudsBorderKeyToken import com.orange.ouds.theme.tokens.OudsColorKeyToken -import com.orange.ouds.theme.tokens.OudsElevationKeyToken +import com.orange.ouds.theme.tokens.OudsSizeKeyToken import com.orange.ouds.theme.tokens.OudsSpaceKeyToken -import com.orange.ouds.theme.tokens.OudsTypographyKeyToken +import com.orange.ouds.tokens.global.raw.DimensionRawTokens open class OudsButtonTokens( - val containerColor: OudsColorKeyToken = OudsColorKeyToken.Background.Brand.Primary, - val contentColor: OudsColorKeyToken = OudsColorKeyToken.Content.Brand.Primary, - val disabledContainerColor: OudsColorKeyToken = OudsColorKeyToken.Action.Disabled, - val disabledContentColor: OudsColorKeyToken = OudsColorKeyToken.Content.Disabled, - val cornerRadius: OudsBorderKeyToken.Radius = OudsBorderKeyToken.Radius.None, - val defaultElevation: OudsElevationKeyToken = OudsElevationKeyToken.None, - val pressedElevation: OudsElevationKeyToken = OudsElevationKeyToken.None, - val focusedElevation: OudsElevationKeyToken = OudsElevationKeyToken.None, - val hoveredElevation: OudsElevationKeyToken = OudsElevationKeyToken.None, - val disabledElevation: OudsElevationKeyToken = OudsElevationKeyToken.None, - val labelStyle: OudsTypographyKeyToken = OudsTypographyKeyToken.Body.Strong.Large, - val verticalContentPadding: OudsSpaceKeyToken.Fixed = OudsSpaceKeyToken.Fixed.Smash, - val horizontalContentPadding: OudsSpaceKeyToken.Fixed = OudsSpaceKeyToken.Fixed.Tall + val borderWidthDefault: OudsBorderKeyToken.Width = OudsBorderKeyToken.Width.Default, + val borderWidthDefaultInteraction: OudsBorderKeyToken.Width = OudsBorderKeyToken.Width.Medium, + val borderWidthMinimal: OudsBorderKeyToken.Width = OudsBorderKeyToken.Width.None, + val borderWidthMinimalInteraction: OudsBorderKeyToken.Width = OudsBorderKeyToken.Width.None, + val borderRadius: OudsBorderKeyToken.Radius = OudsBorderKeyToken.Radius.None, + val spacePaddingInlineStartIconEnd: OudsSpaceKeyToken.PaddingInline = OudsSpaceKeyToken.PaddingInline.Taller, + val spacePaddingInlineEndIconStart: OudsSpaceKeyToken.PaddingInline = OudsSpaceKeyToken.PaddingInline.Taller, + val spacePaddingInlineIconNone: OudsSpaceKeyToken.PaddingInline = OudsSpaceKeyToken.PaddingInline.Tallest, + val spacePaddingInlineIconStart: OudsSpaceKeyToken.PaddingInline = OudsSpaceKeyToken.PaddingInline.WithIcon.Tall, + val spacePaddingInlineArrowStart: OudsSpaceKeyToken.PaddingInline = OudsSpaceKeyToken.PaddingInline.WithArrow.Tall, + val spacePaddingInlineArrowEnd: OudsSpaceKeyToken.PaddingInline = OudsSpaceKeyToken.PaddingInline.WithArrow.Tall, + val spacePaddingBlock: OudsSpaceKeyToken.PaddingBlock = OudsSpaceKeyToken.PaddingBlock.Medium, + val spaceInsetIconAlone: OudsSpaceKeyToken.Inset = OudsSpaceKeyToken.Inset.Medium, + val spaceColumnGapIcon: OudsSpaceKeyToken.ColumnGap = OudsSpaceKeyToken.ColumnGap.WithIcon.Medium, + val spaceColumnGapArrow: OudsSpaceKeyToken.ColumnGap = OudsSpaceKeyToken.ColumnGap.WithArrow.Short, + val sizeIcon: OudsSizeKeyToken.Icon = OudsSizeKeyToken.Icon.WithLabelLarge.SizeShorter, + val sizeIconOnly: OudsSizeKeyToken.Icon = OudsSizeKeyToken.Icon.WithLabelLarge.SizeShort, + val sizeLoader: OudsSizeKeyToken.Icon = OudsSizeKeyToken.Icon.WithLabelLarge.SizeShorter, + val sizeMaxHeight: Float = DimensionRawTokens.dimension600, + val sizeMinHeight: Float = DimensionRawTokens.dimension600, + val sizeMinWidth: Float = DimensionRawTokens.dimension600, + val colorBgDefaultEnabled: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgDefaultHover: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgDefaultPressed: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgDefaultDisabled: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgDefaultLoading: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgDefaultFocus: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgMinimalEnabled: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgMinimalHover: OudsColorKeyToken = OudsColorKeyToken.Action.Secondary.Hover, + val colorBgMinimalPressed: OudsColorKeyToken = OudsColorKeyToken.Action.Secondary.Pressed, + val colorBgMinimalDisabled: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBgMinimalLoading: OudsColorKeyToken = OudsColorKeyToken.Action.Secondary.Loading, + val colorBgMinimalFocus: OudsColorKeyToken = OudsColorKeyToken.Action.Secondary.Focus, + val colorContentDefaultEnabled: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Enabled, + val colorContentDefaultHover: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Hover, + val colorContentDefaultPressed: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Pressed, + val colorContentDefaultDisabled: OudsColorKeyToken = OudsColorKeyToken.Action.Disabled, + val colorContentDefaultLoading: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Loading, + val colorContentDefaultFocus: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Focus, + val colorContentMinimalEnabled: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Enabled, + val colorContentMinimalHover: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Hover, + val colorContentMinimalPressed: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Pressed, + val colorContentMinimalDisabled: OudsColorKeyToken = OudsColorKeyToken.Action.Disabled, + val colorContentMinimalLoading: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Loading, + val colorContentMinimalFocus: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Focus, + val colorBorderDefaultEnabled: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Enabled, + val colorBorderDefaultHover: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Hover, + val colorBorderDefaultPressed: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Pressed, + val colorBorderDefaultDisabled: OudsColorKeyToken = OudsColorKeyToken.Action.Disabled, + val colorBorderDefaultLoading: OudsColorKeyToken = OudsColorKeyToken.Action.Primary.Loading, + val colorBorderMinimalEnabled: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBorderMinimalHover: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBorderMinimalPressed: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBorderMinimalDisabled: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, + val colorBorderMinimalLoading: OudsColorKeyToken = OudsColorKeyToken.Transparent.Default, ) \ No newline at end of file diff --git a/theme-contract/src/main/java/com/orange/ouds/theme/tokens/semantic/OudsBorderSemanticTokens.kt b/theme-contract/src/main/java/com/orange/ouds/theme/tokens/semantic/OudsBorderSemanticTokens.kt index ae4dd5e9..ab822f1a 100644 --- a/theme-contract/src/main/java/com/orange/ouds/theme/tokens/semantic/OudsBorderSemanticTokens.kt +++ b/theme-contract/src/main/java/com/orange/ouds/theme/tokens/semantic/OudsBorderSemanticTokens.kt @@ -15,20 +15,20 @@ package com.orange.ouds.theme.tokens.semantic import com.orange.ouds.tokens.global.raw.BorderRawTokens data class OudsBorderSemanticTokens( - val radiusDefault: Float = BorderRawTokens.borderRadius0, - val radiusMedium: Float = BorderRawTokens.borderRadius150, - val radiusNone: Float = BorderRawTokens.borderRadius0, - val radiusPill: Float = BorderRawTokens.borderRadius9999, - val radiusShort: Float = BorderRawTokens.borderRadius75, - val radiusTall: Float = BorderRawTokens.borderRadius300, - val styleDefault: String = BorderRawTokens.borderStyleSolid, - val styleDrag: String = BorderRawTokens.borderStyleDashed, - val widthDefault: Float = BorderRawTokens.borderWidth25, - val widthFocus: Float = BorderRawTokens.borderWidth50, - val widthFocusInset: Float = BorderRawTokens.borderWidth100, - val widthMedium: Float = BorderRawTokens.borderWidth50, - val widthNone: Float = BorderRawTokens.borderWidth0, - val widthThick: Float = BorderRawTokens.borderWidth75, - val widthThicker: Float = BorderRawTokens.borderWidth100, - val widthThin: Float = BorderRawTokens.borderWidth25 + val radiusDefault: Float = BorderRawTokens.borderRadius0, + val radiusMedium: Float = BorderRawTokens.borderRadius150, + val radiusNone: Float = BorderRawTokens.borderRadius0, + val radiusPill: Float = BorderRawTokens.borderRadius9999, + val radiusShort: Float = BorderRawTokens.borderRadius75, + val radiusTall: Float = BorderRawTokens.borderRadius300, + val styleDefault: String = BorderRawTokens.borderStyleSolid, + val styleDrag: String = BorderRawTokens.borderStyleDashed, + val widthDefault: Float = BorderRawTokens.borderWidth25, + val widthFocus: Float = BorderRawTokens.borderWidth100, + val widthFocusInset: Float = BorderRawTokens.borderWidth50, + val widthMedium: Float = BorderRawTokens.borderWidth50, + val widthNone: Float = BorderRawTokens.borderWidth0, + val widthThick: Float = BorderRawTokens.borderWidth75, + val widthThicker: Float = BorderRawTokens.borderWidth100, + val widthThin: Float = BorderRawTokens.borderWidth25 ) diff --git a/theme-orange-country/src/main/java/com/orange/ouds/theme/orangecountry/OrangeCountryTheme.kt b/theme-orange-country/src/main/java/com/orange/ouds/theme/orangecountry/OrangeCountryTheme.kt index 116a6571..25e0da9e 100644 --- a/theme-orange-country/src/main/java/com/orange/ouds/theme/orangecountry/OrangeCountryTheme.kt +++ b/theme-orange-country/src/main/java/com/orange/ouds/theme/orangecountry/OrangeCountryTheme.kt @@ -38,6 +38,12 @@ class OrangeCountryTheme : OrangeTheme() { override val componentsTokens: OudsComponentsTokens get() = OudsComponentsTokens( - button = OudsButtonTokens(containerColor = OudsColorKeyToken.Background.Status.Accent.Emphasized, cornerRadius = OudsBorderKeyToken.Radius.Short) + button = OudsButtonTokens( + colorBgDefaultEnabled = OudsColorKeyToken.Background.Status.Accent.Emphasized, + colorBgDefaultFocus = OudsColorKeyToken.Background.Status.Accent.Emphasized, + colorBgDefaultHover = OudsColorKeyToken.Background.Status.Accent.Emphasized, + colorBgDefaultPressed = OudsColorKeyToken.Background.Status.Accent.Emphasized, + borderRadius = OudsBorderKeyToken.Radius.Short + ) ) } \ No newline at end of file diff --git a/theme-white-label/src/main/java/com/orange/ouds/theme/whitelabel/WhiteLabelTheme.kt b/theme-white-label/src/main/java/com/orange/ouds/theme/whitelabel/WhiteLabelTheme.kt index d53f0b38..55891257 100644 --- a/theme-white-label/src/main/java/com/orange/ouds/theme/whitelabel/WhiteLabelTheme.kt +++ b/theme-white-label/src/main/java/com/orange/ouds/theme/whitelabel/WhiteLabelTheme.kt @@ -16,9 +16,7 @@ import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import com.orange.ouds.theme.OudsThemeContract import com.orange.ouds.theme.tokens.OudsBorderKeyToken -import com.orange.ouds.theme.tokens.OudsElevationKeyToken import com.orange.ouds.theme.tokens.OudsSpaceKeyToken -import com.orange.ouds.theme.tokens.OudsTypographyKeyToken import com.orange.ouds.theme.tokens.components.OudsButtonTokens import com.orange.ouds.theme.tokens.components.OudsComponentsTokens import com.orange.ouds.theme.tokens.semantic.OudsColorSemanticTokens @@ -40,13 +38,9 @@ open class WhiteLabelTheme : OudsThemeContract { override val componentsTokens: OudsComponentsTokens get() = OudsComponentsTokens( button = OudsButtonTokens( - cornerRadius = OudsBorderKeyToken.Radius.Pill, - defaultElevation = OudsElevationKeyToken.OverlayDefault, - focusedElevation = OudsElevationKeyToken.OverlayDefault, - hoveredElevation = OudsElevationKeyToken.OverlayDefault, - labelStyle = OudsTypographyKeyToken.Body.Default.Large, - verticalContentPadding = OudsSpaceKeyToken.Fixed.Shortest, - horizontalContentPadding = OudsSpaceKeyToken.Fixed.Short + borderRadius = OudsBorderKeyToken.Radius.Pill, + spacePaddingBlock = OudsSpaceKeyToken.PaddingBlock.Shorter, + spacePaddingInlineIconNone = OudsSpaceKeyToken.PaddingInline.Short ) ) }