diff --git a/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/popover/PopoverConfigurator.kt b/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/popover/PopoverConfigurator.kt new file mode 100644 index 000000000..63922497e --- /dev/null +++ b/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/popover/PopoverConfigurator.kt @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2023 Adevinta + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.adevinta.spark.catalog.configurator.samples.popover + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.adevinta.spark.SparkTheme +import com.adevinta.spark.catalog.model.Configurator +import com.adevinta.spark.catalog.themes.SegmentedButton +import com.adevinta.spark.catalog.util.SampleSourceUrl +import com.adevinta.spark.components.buttons.ButtonOutlined +import com.adevinta.spark.components.iconbuttons.IconButtonFilled +import com.adevinta.spark.components.image.Illustration +import com.adevinta.spark.components.image.Image +import com.adevinta.spark.components.menu.DropdownMenuItem +import com.adevinta.spark.components.popover.Popover +import com.adevinta.spark.components.popover.newapi.TooltipState +import com.adevinta.spark.components.popover.newapi.rememberTooltipState +import com.adevinta.spark.components.spacer.VerticalSpacer +import com.adevinta.spark.components.text.Text +import com.adevinta.spark.components.textfields.SelectTextField +import com.adevinta.spark.components.toggles.SwitchLabelled +import com.adevinta.spark.icons.BurgerMenu +import com.adevinta.spark.icons.SparkIcons +import com.adevinta.spark.icons.Store +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +public val PopoverConfigurator: Configurator = Configurator( + name = "Popover", + description = "Popover configuration", + sourceUrl = "$SampleSourceUrl/PopoverSamples.kt", +) { + PopoverSample() +} + +@OptIn(ExperimentalMaterial3Api::class) +@Preview( + showBackground = true, +) +@Composable +private fun PopoverSample() { + val scrollState = rememberScrollState() + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.verticalScroll(scrollState), + ) { + var isDismissButtonEnabled by remember { mutableStateOf(true) } + var popoverContentExample by remember { mutableStateOf(PopoverContentExamples.TextList) } + var popoverTriggerExample by remember { mutableStateOf(PopoverTriggerExamples.Button) } + val popoverState = rememberTooltipState(isPersistent = true) + val scope = rememberCoroutineScope() + + SwitchLabelled( + checked = isDismissButtonEnabled, + onCheckedChange = { + isDismissButtonEnabled = it + }, + ) { + Text( + text = "Show dismiss icon", + modifier = Modifier.fillMaxWidth(), + ) + } + + val contentExamples = PopoverContentExamples.entries.toTypedArray() + var expanded by remember { mutableStateOf(false) } + SelectTextField( + modifier = Modifier.fillMaxWidth(), + value = popoverContentExample.name, + onValueChange = {}, + readOnly = true, + label = "Popover Content Example", + expanded = expanded, + onExpandedChange = { expanded = !expanded }, + onDismissRequest = { expanded = false }, + dropdownContent = { + contentExamples.forEach { + DropdownMenuItem( + text = { Text(it.name) }, + onClick = { + popoverContentExample = it + expanded = false + }, + ) + } + }, + ) + Column { + Text( + text = "Popover Anchor", + modifier = Modifier.padding(bottom = 8.dp), + style = SparkTheme.typography.body2.copy(fontWeight = FontWeight.Bold), + ) + val triggerExamples = PopoverTriggerExamples.entries.toTypedArray() + val contentSidesLabel = triggerExamples.map { it.name } + SegmentedButton( + options = contentSidesLabel, + selectedOption = popoverTriggerExample.name, + onOptionSelect = { + popoverTriggerExample = PopoverTriggerExamples.valueOf(it) + }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + ) + } + + VerticalSpacer(40.dp) + + ConfiguredPopover(scope, popoverState, isDismissButtonEnabled, popoverContentExample, popoverTriggerExample) + } +} + +@Composable +private fun ConfiguredPopover( + scope: CoroutineScope, + popoverState: TooltipState, + isDismissButtonEnabled: Boolean, + popoverContentExample: PopoverContentExamples, + popoverTriggerExample: PopoverTriggerExamples, +) { + Popover( + popoverState = popoverState, + popoverContent = { + when (popoverContentExample) { + PopoverContentExamples.TextList -> LazyColumn { + items(5) { index -> + Box(modifier = Modifier.padding(all = 4.dp)) { + Text(text = "Text: $index") + } + } + } + + PopoverContentExamples.Text -> Column { + Text( + text = "Title", + modifier = Modifier.padding(bottom = 16.dp), + style = SparkTheme.typography.headline1.copy(fontWeight = FontWeight.Bold), + ) + Text( + text = "Do you want to have this cookie now?", + modifier = Modifier.padding(bottom = 16.dp), + style = SparkTheme.typography.body2.copy(fontWeight = FontWeight.Bold), + ) + Text( + text = "Text Link", + textDecoration = TextDecoration.Underline, + style = SparkTheme.typography.body1.copy(fontWeight = FontWeight.Bold) + .copy(color = SparkTheme.colors.accent), + ) + } + + PopoverContentExamples.Image -> Image( + contentScale = ContentScale.Crop, + modifier = Modifier.height(500.dp), + model = "https://t.ly/gio0G", + contentDescription = null, + ) + + PopoverContentExamples.Illustration -> Illustration( + sparkIcon = SparkIcons.Store, + contentDescription = null, + + modifier = Modifier.size(100.dp), + ) + } + }, + isDismissButtonEnabled = isDismissButtonEnabled, + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(4.dp), + contentAlignment = Alignment.Center, + ) { + when (popoverTriggerExample) { + PopoverTriggerExamples.Button -> { + ButtonOutlined( + text = "Display Popover", + onClick = { scope.launch { popoverState.show() } }, + ) + } + + PopoverTriggerExamples.Icon -> { + IconButtonFilled( + onClick = { scope.launch { popoverState.show() } }, + icon = SparkIcons.BurgerMenu, + contentDescription = "Burger Menu", + ) + } + } + } + } +} + +private enum class PopoverContentExamples { + TextList, + Image, + Illustration, + Text, +} + +private enum class PopoverTriggerExamples { + Button, + Icon, +} diff --git a/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt b/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt index ac1b9446b..c34fbe26f 100644 --- a/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt +++ b/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt @@ -27,6 +27,7 @@ import com.adevinta.spark.catalog.R import com.adevinta.spark.catalog.configurator.samples.buttons.ButtonsConfigurator import com.adevinta.spark.catalog.configurator.samples.buttons.IconButtonsConfigurator import com.adevinta.spark.catalog.configurator.samples.buttons.IconToggleButtonsConfigurator +import com.adevinta.spark.catalog.configurator.samples.popover.PopoverConfigurator import com.adevinta.spark.catalog.configurator.samples.rating.RatingsConfigurator import com.adevinta.spark.catalog.configurator.samples.tabs.TabsConfigurator import com.adevinta.spark.catalog.configurator.samples.tags.TagsConfigurator @@ -134,14 +135,14 @@ private val IconToggleButtons = Component( private val Popovers = Component( id = nextId(), name = "Popovers", - illustration = R.drawable.illu_component_tokens, + illustration = R.drawable.illustration_popover, tintIcon = false, description = R.string.component_popovers_description, guidelinesUrl = "$ComponentGuidelinesUrl/p/88a08c-popover/b/904ceb", docsUrl = "$PackageSummaryUrl/com.adevinta.spark.popover/index.html", sourceUrl = "$SparkSourceUrl/kotlin/com/adevinta/popover/Color.kt", examples = PopoverExamples, - configurator = null, + configurator = PopoverConfigurator, ) private val RadioButtons = Component( diff --git a/catalog/src/main/res/drawable/illustration_popover.xml b/catalog/src/main/res/drawable/illustration_popover.xml new file mode 100644 index 000000000..7f7ced162 --- /dev/null +++ b/catalog/src/main/res/drawable/illustration_popover.xml @@ -0,0 +1,49 @@ + + + + + + + + + diff --git a/spark/src/main/kotlin/com/adevinta/spark/components/popover/Popover.kt b/spark/src/main/kotlin/com/adevinta/spark/components/popover/Popover.kt index e70a3b839..84400c63f 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/components/popover/Popover.kt +++ b/spark/src/main/kotlin/com/adevinta/spark/components/popover/Popover.kt @@ -74,7 +74,7 @@ import kotlinx.coroutines.launch * @param popoverContent the composable that will be used to populate the Popover's content. * @param isDismissButtonEnabled [Boolean] that determines if we show a dismiss iconbutton on the Popover, * @param popoverState handles the state of the Popover's visibility. - * @param actionContent the composable that the Popover will anchor to. + * @param anchorContent the composable that the Popover will anchor to. */ @ExperimentalSparkApi @@ -85,7 +85,7 @@ public fun Popover( modifier: Modifier = Modifier, // ignored isDismissButtonEnabled: Boolean = false, popoverState: TooltipState = rememberTooltipState(isPersistent = true), - actionContent: @Composable () -> Unit, + anchorContent: @Composable () -> Unit, ) { val shape: Shape = TooltipDefaults.richTooltipContainerShape /** @@ -109,7 +109,9 @@ public fun Popover( ) { Row { Box( - modifier = Modifier.padding(all = PopoverContentPadding), + modifier = Modifier + .padding(all = PopoverContentPadding) + .weight(1.0f, false), ) { CompositionLocalProvider( content = popoverContent, @@ -117,10 +119,12 @@ public fun Popover( } if (isDismissButtonEnabled) { IconButtonGhost( - modifier = Modifier.padding( - top = PopoverDismissButtonPadding, - end = PopoverDismissButtonPadding, - ), + modifier = Modifier + .padding( + top = PopoverDismissButtonPadding, + end = PopoverDismissButtonPadding, + ) + .weight(2.0f, true), icon = SparkIcons.Close, onClick = { popoverState.dismiss() }, ) @@ -129,7 +133,7 @@ public fun Popover( } }, state = popoverState, - content = actionContent, + content = anchorContent, ) } @@ -143,7 +147,7 @@ private fun PopoverPreview( @PreviewParameter(ThemeProvider::class) theme: ThemeVariant, ) { PreviewTheme(theme) { - val state = rememberTooltipState(isPersistent = true) + val popoverState = rememberTooltipState(isPersistent = true) val scope = rememberCoroutineScope() Column( @@ -173,11 +177,11 @@ private fun PopoverPreview( } }, isDismissButtonEnabled = true, - popoverState = state, + popoverState = popoverState, ) { ButtonOutlined( text = "Display Popover", - onClick = { scope.launch { state.show() } }, + onClick = { scope.launch { popoverState.show() } }, ) } }