Browse Source

[Compound] Implement Snackbars based on designs (#1054)

* Make `InternalButton` internal instead of private so it can be customised.

Also, change the `ButtonColors.contentColor` for text buttons to `LocalContentColor.current` by default.

* Add temporary color for Snackbar action label

* Implement `Snackbar` component based on Compound

* Propagate changes to all other components

* Use right Preview annotation config

* Move `ButtonVisuals` to their own file

* Update screenshots

* Make previews internal

* Update screenshots

* Set a custom token for contentColor in AppBars

* Change 'Label' to 'Action' in the previews

* Add changelog

* Update screenshots

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
pull/1063/head
Jorge Martin Espinosa 1 year ago committed by GitHub
parent
commit
9e1ff513e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      changelog.d/1054.misc
  2. 2
      features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
  3. 4
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
  4. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
  5. 14
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
  6. 11
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
  7. 11
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
  8. 2
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
  9. 53
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt
  10. 1
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt
  11. 2
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
  12. 6
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
  13. 143
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
  14. 23
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
  15. 147
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt
  16. 23
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
  17. 19
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt
  18. 5
      libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt
  19. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
  20. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
  21. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
  22. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
  23. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
  24. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
  25. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
  26. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
  27. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
  28. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
  29. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
  30. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
  31. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
  32. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
  33. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
  34. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
  35. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
  36. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
  37. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
  38. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png
  39. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png
  40. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png
  41. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
  42. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
  43. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png
  44. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png
  45. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png
  46. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png
  47. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png
  48. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png
  49. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png

1
changelog.d/1054.misc

@ -0,0 +1 @@
Compound: implement Snackbar component.

2
features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt

@ -195,7 +195,7 @@ private fun AnalyticsOptInFooter(
) )
TextButton( TextButton(
text = stringResource(id = CommonStrings.action_not_now), text = stringResource(id = CommonStrings.action_not_now),
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
onClick = onTermsDeclined, onClick = onTermsDeclined,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )

4
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt

@ -135,7 +135,7 @@ internal fun DefaultInviteSummaryRow(
text = stringResource(CommonStrings.action_decline), text = stringResource(CommonStrings.action_decline),
onClick = onDeclineClicked, onClick = onDeclineClicked,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
) )
Spacer(modifier = Modifier.width(12.dp)) Spacer(modifier = Modifier.width(12.dp))
@ -144,7 +144,7 @@ internal fun DefaultInviteSummaryRow(
text = stringResource(CommonStrings.action_accept), text = stringResource(CommonStrings.action_accept),
onClick = onAcceptClicked, onClick = onAcceptClicked,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
) )
} }
} }

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -78,6 +77,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState

14
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt

@ -34,9 +34,6 @@ import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -64,6 +61,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.MediaRequestData
@ -99,15 +97,7 @@ fun MediaViewerView(
eventSink = state.eventSink eventSink = state.eventSink
) )
}, },
snackbarHost = { snackbarHost = { SnackbarHost(snackbarHostState) },
SnackbarHost(snackbarHostState) { data ->
Snackbar(
snackbarData = data,
containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.primary
)
}
},
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier

11
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt

@ -24,8 +24,6 @@ import androidx.compose.material.icons.outlined.DeveloperMode
import androidx.compose.material.icons.outlined.Help import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.InsertChart import androidx.compose.material.icons.outlined.InsertChart
import androidx.compose.material.icons.outlined.VerifiedUser import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -41,6 +39,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.LargeHeightPreview import io.element.android.libraries.designsystem.preview.LargeHeightPreview
import io.element.android.libraries.designsystem.theme.components.Divider import io.element.android.libraries.designsystem.theme.components.Divider
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.MatrixUserProvider import io.element.android.libraries.matrix.ui.components.MatrixUserProvider
@ -65,13 +64,7 @@ fun PreferencesRootView(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_settings), title = stringResource(id = CommonStrings.common_settings),
snackbarHost = { snackbarHost = { SnackbarHost(snackbarHostState) }
SnackbarHost(snackbarHostState) { data ->
Snackbar(
snackbarData = data,
)
}
}
) { ) {
UserPreferences(state.myUser) UserPreferences(state.myUser)
if (state.showCompleteVerification) { if (state.showCompleteVerification) {

11
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt

@ -28,8 +28,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -59,6 +57,7 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.designsystem.R as DrawableR import io.element.android.libraries.designsystem.R as DrawableR
@ -227,13 +226,7 @@ fun RoomListContent(
) )
} }
}, },
snackbarHost = { snackbarHost = { SnackbarHost(snackbarHostState) },
SnackbarHost(snackbarHostState) { data ->
Snackbar(
snackbarData = data,
)
}
},
) )
} }

2
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt

@ -83,7 +83,7 @@ internal fun RequestVerificationHeader(
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Button( Button(
text = stringResource(CommonStrings.action_continue), text = stringResource(CommonStrings.action_continue),
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = onVerifyClicked, onClick = onVerifyClicked,
) )

53
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt

@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 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.button
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.TextButton
/**
* A sealed class that represents the different visual styles that a button can have.
*/
sealed interface ButtonVisuals {
val action: () -> Unit
/**
* Creates a [Button] composable based on the visual state.
*/
@Composable
fun Composable()
data class Text(val text: String, override val action: () -> Unit) : ButtonVisuals {
@Composable
override fun Composable() {
TextButton(text = text, onClick = action)
}
}
data class Icon(val iconSource: IconSource, override val action: () -> Unit) : ButtonVisuals {
@Composable
override fun Composable() {
IconButton(onClick = action) {
Icon(iconSource.getPainter(), iconSource.contentDescription)
}
}
}
}

1
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt

@ -30,6 +30,7 @@ object PreviewGroup {
const val Preferences = "Preferences" const val Preferences = "Preferences"
const val Progress = "Progress Indicators" const val Progress = "Progress Indicators"
const val Search = "Search views" const val Search = "Search views"
const val Snackbars = "Snackbars"
const val Sliders = "Sliders" const val Sliders = "Sliders"
const val Text = "Text" const val Text = "Text"
const val TextFields = "TextFields" const val TextFields = "TextFields"

2
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt

@ -78,7 +78,7 @@ private fun ContentToPreview() {
WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) { WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) {
OutlinedButton( OutlinedButton(
text = "A Button with rulers on it!", text = "A Button with rulers on it!",
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
onClick = {}, onClick = {},
) )
} }

6
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt

@ -72,19 +72,19 @@ internal fun SimpleAlertDialogContent(
// Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f // Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f
TextButton( TextButton(
text = thirdButtonText, text = thirdButtonText,
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
onClick = onThirdButtonClicked, onClick = onThirdButtonClicked,
) )
} }
TextButton( TextButton(
text = cancelText, text = cancelText,
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
onClick = onCancelClicked, onClick = onCancelClicked,
) )
if (submitText != null) { if (submitText != null) {
Button( Button(
text = submitText, text = submitText,
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
onClick = onSubmitClicked, onClick = onSubmitClicked,
) )
} }

143
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt

@ -32,6 +32,7 @@ import androidx.compose.foundation.progressSemantics
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Share import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -41,6 +42,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter
@ -60,10 +62,19 @@ fun Button(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
buttonSize: ButtonSize = ButtonSize.Large, size: ButtonSize = ButtonSize.Large,
showProgress: Boolean = false, showProgress: Boolean = false,
leadingIcon: IconSource? = null, leadingIcon: IconSource? = null,
) = ButtonInternal(text, onClick, ButtonStyle.Filled, modifier, enabled, buttonSize, showProgress, leadingIcon) ) = ButtonInternal(
text = text,
onClick = onClick,
style = ButtonStyle.Filled,
modifier = modifier,
enabled = enabled,
size = size,
showProgress = showProgress,
leadingIcon = leadingIcon
)
@Composable @Composable
fun OutlinedButton( fun OutlinedButton(
@ -71,10 +82,19 @@ fun OutlinedButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
buttonSize: ButtonSize = ButtonSize.Large, size: ButtonSize = ButtonSize.Large,
showProgress: Boolean = false, showProgress: Boolean = false,
leadingIcon: IconSource? = null, leadingIcon: IconSource? = null,
) = ButtonInternal(text, onClick, ButtonStyle.Outlined, modifier, enabled, buttonSize, showProgress, leadingIcon) ) = ButtonInternal(
text = text,
onClick = onClick,
style = ButtonStyle.Outlined,
modifier = modifier,
enabled = enabled,
size = size,
showProgress = showProgress,
leadingIcon = leadingIcon
)
@Composable @Composable
fun TextButton( fun TextButton(
@ -82,17 +102,27 @@ fun TextButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
buttonSize: ButtonSize = ButtonSize.Large, size: ButtonSize = ButtonSize.Large,
showProgress: Boolean = false, showProgress: Boolean = false,
leadingIcon: IconSource? = null, leadingIcon: IconSource? = null,
) = ButtonInternal(text, onClick, ButtonStyle.Text, modifier, enabled, buttonSize, showProgress, leadingIcon) ) = ButtonInternal(
text = text,
onClick = onClick,
style = ButtonStyle.Text,
modifier = modifier,
enabled = enabled,
size = size,
showProgress = showProgress,
leadingIcon = leadingIcon
)
@Composable @Composable
private fun ButtonInternal( internal fun ButtonInternal(
text: String, text: String,
onClick: () -> Unit, onClick: () -> Unit,
style: ButtonStyle, style: ButtonStyle,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = style.getColors(),
enabled: Boolean = true, enabled: Boolean = true,
size: ButtonSize = ButtonSize.Large, size: ButtonSize = ButtonSize.Large,
showProgress: Boolean = false, showProgress: Boolean = false,
@ -123,21 +153,6 @@ private fun ButtonInternal(
ButtonStyle.Text -> RectangleShape ButtonStyle.Text -> RectangleShape
} }
val colors = when (style) {
ButtonStyle.Filled -> ButtonDefaults.buttonColors(
containerColor = ElementTheme.materialColors.primary,
contentColor = ElementTheme.materialColors.onPrimary,
disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled,
disabledContentColor = ElementTheme.colors.textOnSolidPrimary
)
ButtonStyle.Outlined, ButtonStyle.Text -> ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = ElementTheme.materialColors.primary,
disabledContainerColor = Color.Transparent,
disabledContentColor = ElementTheme.colors.textDisabled,
)
}
val border = when (style) { val border = when (style) {
ButtonStyle.Filled, ButtonStyle.Text -> null ButtonStyle.Filled, ButtonStyle.Text -> null
ButtonStyle.Outlined -> BorderStroke( ButtonStyle.Outlined -> BorderStroke(
@ -202,8 +217,10 @@ private fun ButtonInternal(
} }
sealed interface IconSource { sealed interface IconSource {
data class Resource(val id: Int) : IconSource val contentDescription: String?
data class Vector(val vector: ImageVector) : IconSource
data class Resource(val id: Int, override val contentDescription: String? = null) : IconSource
data class Vector(val vector: ImageVector, override val contentDescription: String? = null) : IconSource
@Composable @Composable
fun getPainter(): Painter = when (this) { fun getPainter(): Painter = when (this) {
@ -216,16 +233,38 @@ enum class ButtonSize {
Medium, Large Medium, Large
} }
private enum class ButtonStyle { internal enum class ButtonStyle {
Filled, Outlined, Text Filled, Outlined, Text;
@Composable
fun getColors(): ButtonColors = when (this) {
Filled -> ButtonDefaults.buttonColors(
containerColor = ElementTheme.materialColors.primary,
contentColor = ElementTheme.materialColors.onPrimary,
disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled,
disabledContentColor = ElementTheme.colors.textOnSolidPrimary
)
Outlined -> ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = ElementTheme.materialColors.primary,
disabledContainerColor = Color.Transparent,
disabledContentColor = ElementTheme.colors.textDisabled,
)
Text -> ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = if (LocalContentColor.current.isSpecified) LocalContentColor.current else ElementTheme.materialColors.primary,
disabledContainerColor = Color.Transparent,
disabledContentColor = ElementTheme.colors.textDisabled,
)
}
} }
@Preview(group = PreviewGroup.Buttons) @Preview(group = PreviewGroup.Buttons)
@Composable @Composable
internal fun FilledButtonMediumPreview() { internal fun FilledButtonMediumPreview() {
ButtonCombinationPreview( ButtonCombinationPreview(
buttonStyle = ButtonStyle.Filled, style = ButtonStyle.Filled,
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
) )
} }
@ -233,8 +272,8 @@ internal fun FilledButtonMediumPreview() {
@Composable @Composable
internal fun FilledButtonLargePreview() { internal fun FilledButtonLargePreview() {
ButtonCombinationPreview( ButtonCombinationPreview(
buttonStyle = ButtonStyle.Filled, style = ButtonStyle.Filled,
buttonSize = ButtonSize.Large, size = ButtonSize.Large,
) )
} }
@ -242,8 +281,8 @@ internal fun FilledButtonLargePreview() {
@Composable @Composable
internal fun OutlinedButtonMediumPreview() { internal fun OutlinedButtonMediumPreview() {
ButtonCombinationPreview( ButtonCombinationPreview(
buttonStyle = ButtonStyle.Outlined, style = ButtonStyle.Outlined,
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
) )
} }
@ -251,8 +290,8 @@ internal fun OutlinedButtonMediumPreview() {
@Composable @Composable
internal fun OutlinedButtonLargePreview() { internal fun OutlinedButtonLargePreview() {
ButtonCombinationPreview( ButtonCombinationPreview(
buttonStyle = ButtonStyle.Outlined, style = ButtonStyle.Outlined,
buttonSize = ButtonSize.Large, size = ButtonSize.Large,
) )
} }
@ -260,8 +299,8 @@ internal fun OutlinedButtonLargePreview() {
@Composable @Composable
internal fun TextButtonMediumPreview() { internal fun TextButtonMediumPreview() {
ButtonCombinationPreview( ButtonCombinationPreview(
buttonStyle = ButtonStyle.Text, style = ButtonStyle.Text,
buttonSize = ButtonSize.Medium, size = ButtonSize.Medium,
) )
} }
@ -269,15 +308,15 @@ internal fun TextButtonMediumPreview() {
@Composable @Composable
internal fun TextButtonLargePreview() { internal fun TextButtonLargePreview() {
ButtonCombinationPreview( ButtonCombinationPreview(
buttonStyle = ButtonStyle.Text, style = ButtonStyle.Text,
buttonSize = ButtonSize.Large, size = ButtonSize.Large,
) )
} }
@Composable @Composable
private fun ButtonCombinationPreview( private fun ButtonCombinationPreview(
buttonStyle: ButtonStyle, style: ButtonStyle,
buttonSize: ButtonSize, size: ButtonSize,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
ElementThemedPreview { ElementThemedPreview {
@ -290,24 +329,24 @@ private fun ButtonCombinationPreview(
// Normal // Normal
ButtonRowPreview( ButtonRowPreview(
modifier = Modifier.then(modifier), modifier = Modifier.then(modifier),
buttonStyle = buttonStyle, style = style,
buttonSize = buttonSize, size = size,
) )
// With icon // With icon
ButtonRowPreview( ButtonRowPreview(
modifier = Modifier.then(modifier), modifier = Modifier.then(modifier),
leadingIcon = IconSource.Vector(Icons.Outlined.Share), leadingIcon = IconSource.Vector(Icons.Outlined.Share),
buttonStyle = buttonStyle, style = style,
buttonSize = buttonSize, size = size,
) )
// With progress // With progress
ButtonRowPreview( ButtonRowPreview(
modifier = Modifier.then(modifier), modifier = Modifier.then(modifier),
showProgress = true, showProgress = true,
buttonStyle = buttonStyle, style = style,
buttonSize = buttonSize, size = size,
) )
} }
} }
@ -315,8 +354,8 @@ private fun ButtonCombinationPreview(
@Composable @Composable
private fun ButtonRowPreview( private fun ButtonRowPreview(
buttonStyle: ButtonStyle, style: ButtonStyle,
buttonSize: ButtonSize, size: ButtonSize,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
leadingIcon: IconSource? = null, leadingIcon: IconSource? = null,
showProgress: Boolean = false, showProgress: Boolean = false,
@ -326,8 +365,8 @@ private fun ButtonRowPreview(
text = "A button", text = "A button",
showProgress = showProgress, showProgress = showProgress,
onClick = {}, onClick = {},
style = buttonStyle, style = style,
size = buttonSize, size = size,
leadingIcon = leadingIcon, leadingIcon = leadingIcon,
modifier = Modifier.then(modifier), modifier = Modifier.then(modifier),
) )
@ -336,8 +375,8 @@ private fun ButtonRowPreview(
showProgress = showProgress, showProgress = showProgress,
enabled = false, enabled = false,
onClick = {}, onClick = {},
style = buttonStyle, style = style,
size = buttonSize, size = size,
leadingIcon = leadingIcon, leadingIcon = leadingIcon,
modifier = Modifier.then(modifier), modifier = Modifier.then(modifier),
) )

23
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt

@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -43,7 +49,11 @@ fun MediumTopAppBar(
title = title, title = title,
modifier = modifier, modifier = modifier,
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actions = actions, actions = {
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) {
actions()
}
},
windowInsets = windowInsets, windowInsets = windowInsets,
colors = colors, colors = colors,
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
@ -58,5 +68,14 @@ internal fun MediumTopAppBarPreview() =
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun ContentToPreview() { private fun ContentToPreview() {
MediumTopAppBar(title = { Text(text = "Title") }) MediumTopAppBar(
title = { Text(text = "Title") },
navigationIcon = { BackButton(onClick = {}) },
actions = {
TextButton(text = "Action", onClick = {})
IconButton(onClick = {}) {
Icon(imageVector = Icons.Default.Share, contentDescription = null)
}
}
)
} }

147
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt

@ -0,0 +1,147 @@
/*
* Copyright (c) 2023 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.theme.components
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.SnackbarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.SnackBarLabelColorDark
import io.element.android.libraries.theme.SnackBarLabelColorLight
@Composable
fun Snackbar(
message: String,
modifier: Modifier = Modifier,
action: ButtonVisuals? = null,
dismissAction: ButtonVisuals? = null,
actionOnNewLine: Boolean = false,
shape: Shape = RoundedCornerShape(8.dp),
containerColor: Color = SnackbarDefaults.color,
contentColor: Color = ElementTheme.materialColors.inverseOnSurface,
actionContentColor: Color = actionContentColor(),
dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor,
) {
Snackbar(
modifier = modifier,
action = action?.let { @Composable { it.Composable() } },
dismissAction = dismissAction?.let { @Composable { it.Composable() } },
actionOnNewLine = actionOnNewLine,
shape = shape,
containerColor = containerColor,
contentColor = contentColor,
actionContentColor = actionContentColor,
dismissActionContentColor = dismissActionContentColor,
content = { Text(text = message) },
)
}
@Composable
fun Snackbar(
modifier: Modifier = Modifier,
action: @Composable (() -> Unit)? = null,
dismissAction: @Composable (() -> Unit)? = null,
actionOnNewLine: Boolean = false,
shape: Shape = RoundedCornerShape(8.dp),
containerColor: Color = SnackbarDefaults.color,
contentColor: Color = ElementTheme.materialColors.inverseOnSurface,
actionContentColor: Color = actionContentColor(),
dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor,
content: @Composable () -> Unit
) {
androidx.compose.material3.Snackbar(
modifier = modifier,
action = action,
dismissAction = dismissAction,
actionOnNewLine = actionOnNewLine,
shape = shape,
containerColor = containerColor,
contentColor = contentColor,
actionContentColor = actionContentColor,
dismissActionContentColor = dismissActionContentColor,
content = content,
)
}
// TODO this color is temporary, an `inverse` version should be added to the semantic colors instead
@Composable
private fun actionContentColor(): Color {
return if (ElementTheme.isLightTheme) {
SnackBarLabelColorLight
} else {
SnackBarLabelColorDark
}
}
@Preview(name = "Snackbar", group = PreviewGroup.Snackbars)
@Composable
internal fun SnackbarPreview() {
ElementThemedPreview {
Snackbar(message = "Snackbar supporting text")
}
}
@Preview(name = "Snackbar with action", group = PreviewGroup.Snackbars)
@Composable
internal fun SnackbarWithActionPreview() {
ElementThemedPreview {
Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}))
}
}
@Preview(name = "Snackbar with action and close button", group = PreviewGroup.Snackbars)
@Composable
internal fun SnackbarWithActionAndCloseButtonPreview() {
ElementThemedPreview {
Snackbar(
message = "Snackbar supporting text",
action = ButtonVisuals.Text("Action", {}),
dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {})
)
}
}
@Preview(name = "Snackbar with action on new line", group = PreviewGroup.Snackbars)
@Composable
internal fun SnackbarWithActionOnNewLinePreview() {
ElementThemedPreview {
Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}), actionOnNewLine = true)
}
}
@Preview(name = "Snackbar with action and close button on new line", group = PreviewGroup.Snackbars)
@Composable
internal fun SnackbarWithActionOnNewLineAndCloseButtonPreview() {
ElementThemedPreview {
Snackbar(
message = "Snackbar supporting text",
action = ButtonVisuals.Text("Action", {}),
dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {}),
actionOnNewLine = true
)
}
}

23
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt

@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -43,7 +49,11 @@ fun TopAppBar(
title = title, title = title,
modifier = modifier, modifier = modifier,
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actions = actions, actions = {
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) {
actions()
}
},
windowInsets = windowInsets, windowInsets = windowInsets,
colors = colors, colors = colors,
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
@ -58,5 +68,14 @@ internal fun TopAppBarPreview() =
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun ContentToPreview() { private fun ContentToPreview() {
TopAppBar(title = { Text(text = "Title") }) TopAppBar(
title = { Text(text = "Title") },
navigationIcon = { BackButton(onClick = {}) },
actions = {
TextButton(text = "Action", onClick = {})
IconButton(onClick = {}) {
Icon(imageVector = Icons.Default.Share, contentDescription = null)
}
}
)
} }

19
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt

@ -17,6 +17,8 @@
package io.element.android.libraries.designsystem.utils package io.element.android.libraries.designsystem.utils
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -25,7 +27,11 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.Snackbar
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@ -65,6 +71,19 @@ fun SnackbarDispatcher.collectSnackbarMessageAsState(): State<SnackbarMessage?>
return snackbarMessage.collectAsState(initial = null) return snackbarMessage.collectAsState(initial = null)
} }
@Composable
fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
Snackbar(
message = data.visuals.message,
action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
dismissAction = if (data.visuals.withDismissAction) {
ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), data::dismiss)
} else null,
)
}
}
@Composable @Composable
fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostState { fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostState {
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }

5
libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt

@ -17,6 +17,8 @@
package io.element.android.libraries.theme package io.element.android.libraries.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens
import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens
// ================================================================================================= // =================================================================================================
// IMPORTANT! // IMPORTANT!
@ -26,3 +28,6 @@ import androidx.compose.ui.graphics.Color
// ================================================================================================= // =================================================================================================
val LinkColor = Color(0xFF0086E6) val LinkColor = Color(0xFF0086E6)
val SnackBarLabelColorLight = LightDesignTokens.colorGray700
val SnackBarLabelColorDark = DarkDesignTokens.colorGray700

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save