Compound: add BigIcon, BigCheckmark and PageTitle components (#2574)
* Compound: add `BigIcon`, `BigCheckmark` and `PageTitle` components * Update screenshots --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
8a6020c5a6
commit
998eb7d093
23 changed files with 433 additions and 4 deletions
1
changelog.d/2574.misc
Normal file
1
changelog.d/2574.misc
Normal file
|
|
@ -0,0 +1 @@
|
|||
Compound: add `BigIcon`, `BigCheckmark` and `PageTitle` components.
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.components
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.bigCheckmarkBorderColor
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
/**
|
||||
* Compound component that displays a big checkmark centered in a rounded square.
|
||||
*
|
||||
* @param modifier the modifier to apply to this layout
|
||||
*/
|
||||
@Composable
|
||||
fun BigCheckmark(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.size(120.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = ElementTheme.colors.bgCanvasDefault,
|
||||
border = BorderStroke(1.dp, ElementTheme.colors.bigCheckmarkBorderColor),
|
||||
shadowElevation = 4.dp,
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
modifier = Modifier.size(72.dp),
|
||||
tint = ElementTheme.colors.iconSuccessPrimary,
|
||||
imageVector = CompoundIcons.CheckCircleSolid(),
|
||||
contentDescription = stringResource(CommonStrings.common_success)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun BigCheckmarkPreview() {
|
||||
ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier.padding(10.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
BigCheckmark()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CatchingPokemon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.bigIconDefaultBackgroundColor
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
/**
|
||||
* Compound component that display a big icon centered in a rounded square.
|
||||
*/
|
||||
object BigIcon {
|
||||
/**
|
||||
* The style of the [BigIcon].
|
||||
*/
|
||||
@Immutable
|
||||
sealed interface Style {
|
||||
/**
|
||||
* The default style.
|
||||
*
|
||||
* @param vectorIcon the [ImageVector] to display
|
||||
* @param contentDescription the content description of the icon, if any. It defaults to `null`
|
||||
*/
|
||||
data class Default(val vectorIcon: ImageVector, val contentDescription: String? = null) : Style
|
||||
|
||||
/**
|
||||
* An alert style with a transparent background.
|
||||
*/
|
||||
data object Alert : Style
|
||||
|
||||
/**
|
||||
* An alert style with a tinted background.
|
||||
*/
|
||||
data object AlertSolid : Style
|
||||
|
||||
/**
|
||||
* A success style with a transparent background.
|
||||
*/
|
||||
data object Success : Style
|
||||
|
||||
/**
|
||||
* A success style with a tinted background.
|
||||
*/
|
||||
data object SuccessSolid : Style
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a [BigIcon].
|
||||
*
|
||||
* @param style the style of the icon
|
||||
* @param modifier the modifier to apply to this layout
|
||||
*/
|
||||
@Composable
|
||||
operator fun invoke(
|
||||
style: Style,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val backgroundColor = when (style) {
|
||||
is Style.Default -> ElementTheme.colors.bigIconDefaultBackgroundColor
|
||||
Style.Alert, Style.Success -> Color.Transparent
|
||||
Style.AlertSolid -> ElementTheme.colors.bgCriticalSubtle
|
||||
Style.SuccessSolid -> ElementTheme.colors.bgSuccessSubtle
|
||||
}
|
||||
val icon = when (style) {
|
||||
is Style.Default -> style.vectorIcon
|
||||
Style.Alert, Style.AlertSolid -> CompoundIcons.Error()
|
||||
Style.Success, Style.SuccessSolid -> CompoundIcons.CheckCircleSolid()
|
||||
}
|
||||
val contentDescription = when (style) {
|
||||
is Style.Default -> style.contentDescription
|
||||
Style.Alert, Style.AlertSolid -> stringResource(CommonStrings.common_error)
|
||||
Style.Success, Style.SuccessSolid -> stringResource(CommonStrings.common_success)
|
||||
}
|
||||
val iconTint = when (style) {
|
||||
is Style.Default -> ElementTheme.colors.iconSecondaryAlpha
|
||||
Style.Alert, Style.AlertSolid -> ElementTheme.colors.iconCriticalPrimary
|
||||
Style.Success, Style.SuccessSolid -> ElementTheme.colors.iconSuccessPrimary
|
||||
}
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(64.dp)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(backgroundColor),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(32.dp),
|
||||
tint = iconTint,
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun BigIconPreview() {
|
||||
ElementPreview {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(10.dp)) {
|
||||
val provider = BigIconStylePreviewProvider()
|
||||
for (style in provider.values) {
|
||||
BigIcon(style = style)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BigIconStylePreviewProvider : PreviewParameterProvider<BigIcon.Style> {
|
||||
override val values: Sequence<BigIcon.Style>
|
||||
get() = sequenceOf(
|
||||
BigIcon.Style.Default(Icons.Filled.CatchingPokemon),
|
||||
BigIcon.Style.Alert,
|
||||
BigIcon.Style.AlertSolid,
|
||||
BigIcon.Style.Success,
|
||||
BigIcon.Style.SuccessSolid
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
|
||||
/**
|
||||
* Compound component that displays a big icon, a title, an optional subtitle and an optional call to action component.
|
||||
*
|
||||
* @param title the title to display
|
||||
* @param iconStyle the style of the [BigIcon] to display
|
||||
* @param modifier the modifier to apply to this layout
|
||||
* @param subtitle the optional subtitle to display. It defaults to `null`
|
||||
* @param callToAction the optional call to action component to display. It defaults to `null`
|
||||
*/
|
||||
@Composable
|
||||
fun PageTitle(
|
||||
title: AnnotatedString,
|
||||
iconStyle: BigIcon.Style,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitle: AnnotatedString? = null,
|
||||
callToAction: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 40.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
BigIcon(style = iconStyle)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontHeadingMdBold,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
subtitle?.let {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = it,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
callToAction?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compound component that displays a big icon, a title, an optional subtitle and an optional call to action component.
|
||||
*
|
||||
* @param title the title to display
|
||||
* @param iconStyle the style of the [BigIcon] to display
|
||||
* @param modifier the modifier to apply to this layout
|
||||
* @param subtitle the optional subtitle to display. It defaults to `null`
|
||||
* @param callToAction the optional call to action component to display. It defaults to `null`
|
||||
*/
|
||||
@Composable
|
||||
fun PageTitle(
|
||||
title: String,
|
||||
iconStyle: BigIcon.Style,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitle: String? = null,
|
||||
callToAction: @Composable (() -> Unit)? = null,
|
||||
) = PageTitle(
|
||||
title = AnnotatedString(title),
|
||||
iconStyle = iconStyle,
|
||||
modifier = modifier,
|
||||
subtitle = subtitle?.let { AnnotatedString(it) },
|
||||
callToAction = callToAction
|
||||
)
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TitleWithIconFullPreview(@PreviewParameter(BigIconStylePreviewProvider::class) style: BigIcon.Style) {
|
||||
ElementPreview {
|
||||
PageTitle(
|
||||
modifier = Modifier.padding(top = 24.dp),
|
||||
title = AnnotatedString("Headline"),
|
||||
subtitle = AnnotatedString("Description goes here"),
|
||||
iconStyle = style,
|
||||
callToAction = {
|
||||
TextButton(text = "Learn more", onClick = {})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TitleWithIconMinimalPreview() {
|
||||
ElementPreview {
|
||||
PageTitle(
|
||||
modifier = Modifier.padding(top = 24.dp),
|
||||
title = "Headline",
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.CheckCircleSolid()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,12 @@ package io.element.android.libraries.designsystem.theme
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.annotations.CoreColorToken
|
||||
import io.element.android.compound.previews.ColorListPreview
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.SemanticColors
|
||||
import io.element.android.compound.tokens.generated.internal.DarkColorTokens
|
||||
import io.element.android.compound.tokens.generated.internal.LightColorTokens
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
|
|
@ -138,6 +141,14 @@ val SemanticColors.mentionPillBackground
|
|||
Color(0x26f4f7fa)
|
||||
}
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.bigIconDefaultBackgroundColor
|
||||
get() = if (isLight) LightColorTokens.colorAlphaGray300 else DarkColorTokens.colorAlphaGray300
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.bigCheckmarkBorderColor
|
||||
get() = if (isLight) LightColorTokens.colorGray400 else DarkColorTokens.colorGray400
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun ColorAliasesPreview() = ElementPreview {
|
||||
|
|
@ -155,6 +166,7 @@ internal fun ColorAliasesPreview() = ElementPreview {
|
|||
"progressIndicatorTrackColor" to ElementTheme.colors.progressIndicatorTrackColor,
|
||||
"temporaryColorBgSpecial" to ElementTheme.colors.temporaryColorBgSpecial,
|
||||
"iconSuccessPrimaryBackground" to ElementTheme.colors.iconSuccessPrimaryBackground,
|
||||
"bigIconBackgroundColor" to ElementTheme.colors.bigIconDefaultBackgroundColor,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d3714ab8d509347e90a8db82bd863f1084cfacb500ec0fed04c6fb0a39f15ed6
|
||||
size 10015
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5650e0a5011179d4f1f6d87d893c66b5ac15916fc9d4720b5c3c62567b9206ff
|
||||
size 8729
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:23aea5dbedc9ef43421e593ea6df85781b7b44dcf7199b5e53b4618f2794f198
|
||||
size 13213
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0b0080d19649b4a2059afa04aded3473bfaea3bf714c1381238f83fb19c81641
|
||||
size 12992
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:43e9d3da20a8509449c561da94b9c6ef579d24ac26c088dcd70ee14f17696ae4
|
||||
size 15641
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6738ba137031cf2c571d1a0f803c95a9e07a96f045d5e792194149f5e47f8cfb
|
||||
size 13985
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2d1d6ec215911d23911047ca93249086f9319dd2b8a3eb5d55f3b34a4eef303e
|
||||
size 14938
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8e43ed0d3658a27dbcbd445bbcdb7d4d8a8748d17b4eddaba8f4769efbd54b22
|
||||
size 14142
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9dc541c9f45491263bfd707ab4ac9817c0880449ae480b89bdf3350aaf2e60d5
|
||||
size 15164
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d297bf9fc27ef1f370dfd376fd5c652a11a20c54212ec70f3055ce7df70a9c23
|
||||
size 15159
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4d21c303343018c76ed98e863dafef1c34a07a69a3b4327cfda9597a95c508f1
|
||||
size 13448
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6eb887d3ff278cd90b2e03d613977959cf00a997294d078c4ac6b769f1afebdd
|
||||
size 14738
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a44d081d6897d83ea670b1d6cd06a8f0509dfebd9ee1be89fd7278a1aaaa7cea
|
||||
size 13546
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8d5baa3e15f38b8b961478bfdbf1058ba66b4d813defe6b410392500bba76efb
|
||||
size 14721
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2dc4503ca607eec53c45834a322cd0e62183c778ad89c43f92291e04f7ebfb2e
|
||||
size 9550
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7ecd944412e86daf6ebda5b3f83cc50ba43d591fe550e99de027907babcf7199
|
||||
size 9390
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:567539adb89fefa279c1ea2188d6facc9539b33e29473a15b8fd71792cdf1f1b
|
||||
size 59137
|
||||
oid sha256:096dd17663bcacc9349cd9f31fafff1b76f35bd8d9a7a08d080d8b858b7f3e1d
|
||||
size 62792
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:048b682034cbf46f8ba341a9f9d4a54a18b6bbfb79cc4ca27bb9e42dda1cc3a4
|
||||
size 58440
|
||||
oid sha256:bc08b47e2f2627a751931958e4fd017a24793df71211bbcb7f2ee6d7a9ae1a83
|
||||
size 62530
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue