diff --git a/app/src/main/java/co/yml/coreui/MainActivity.kt b/app/src/main/java/co/yml/coreui/MainActivity.kt index c800d45..e87d1b1 100644 --- a/app/src/main/java/co/yml/coreui/MainActivity.kt +++ b/app/src/main/java/co/yml/coreui/MainActivity.kt @@ -11,9 +11,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.ui.Modifier -import co.yml.coreui.feature.ytag.ui.YTagActivity import co.yml.coreui.core.ui.templates.AppBar import co.yml.coreui.core.ui.theme.CoreUICatalogTheme +import co.yml.coreui.feature.ytag.ui.YTagActivity import co.yml.coreui.ui.R import co.yml.coreui.ui.presentation.CoreUIComponents import dagger.hilt.android.AndroidEntryPoint @@ -30,10 +30,11 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { CoreUICatalogTheme { - Scaffold(modifier = Modifier.fillMaxSize(), + Scaffold( + modifier = Modifier.fillMaxSize(), containerColor = CoreUICatalogTheme.colors.background, - topBar = { AppBar() }) - { + topBar = { AppBar() } + ) { Column( modifier = Modifier .padding(it) diff --git a/app/src/main/java/co/yml/coreui/ui/presentation/CoreUIComponents.kt b/app/src/main/java/co/yml/coreui/ui/presentation/CoreUIComponents.kt index 1d61642..9c5a59f 100644 --- a/app/src/main/java/co/yml/coreui/ui/presentation/CoreUIComponents.kt +++ b/app/src/main/java/co/yml/coreui/ui/presentation/CoreUIComponents.kt @@ -24,7 +24,8 @@ fun CoreUIComponents(title: String, onClick: () -> Unit) { ) ) .fillMaxWidth() - .height(48.dp), onClick = onClick, + .height(48.dp), + onClick = onClick, colors = ButtonDefaults.buttonColors(containerColor = CoreUICatalogTheme.colors.button) ) { Text( diff --git a/core/common/src/test/java/co/yml/coreui/common/TestCoroutineProvider.kt b/core/common/src/test/java/co/yml/coreui/common/TestCoroutineProvider.kt index 818c1da..1a80b21 100644 --- a/core/common/src/test/java/co/yml/coreui/common/TestCoroutineProvider.kt +++ b/core/common/src/test/java/co/yml/coreui/common/TestCoroutineProvider.kt @@ -1,10 +1,10 @@ package co.yml.coreui.common +import co.yml.coreui.core.common.di.DispatcherModule +import co.yml.coreui.core.common.di.DispatcherModule_ProvideNetworkDispatcherFactory.provideNetworkDispatcher import kotlinx.coroutines.test.runTest import org.junit.Assert.assertTrue import org.junit.Test -import co.yml.coreui.core.common.di.DispatcherModule -import co.yml.coreui.core.common.di.DispatcherModule_ProvideNetworkDispatcherFactory.provideNetworkDispatcher /** * Test coroutine provider diff --git a/core/data/src/main/java/co/yml/coreui/core/data/di/RepositoryModule.kt b/core/data/src/main/java/co/yml/coreui/core/data/di/RepositoryModule.kt index 164617e..04c9577 100644 --- a/core/data/src/main/java/co/yml/coreui/core/data/di/RepositoryModule.kt +++ b/core/data/src/main/java/co/yml/coreui/core/data/di/RepositoryModule.kt @@ -11,6 +11,4 @@ import dagger.hilt.components.SingletonComponent */ @Module @InstallIn(SingletonComponent::class) -interface RepositoryModule { - -} +interface RepositoryModule diff --git a/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewContainerTesting.kt b/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewContainerTesting.kt index 96fcf88..7a921eb 100644 --- a/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewContainerTesting.kt +++ b/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewContainerTesting.kt @@ -82,14 +82,14 @@ class TagViewContainerTesting { } @Test - fun tagViewContainer_tags_shown(){ + fun tagViewContainer_tags_shown() { launchTagViewContainer() composeTestRule.onNodeWithText("Tag 1").assertIsDisplayed() } @Test - fun tagViewContainer_with_less_space_more_tag_shown(){ + fun tagViewContainer_with_less_space_more_tag_shown() { val tagViewContainerModifiers = TagViewContainerModifiers.Builder() .width(150.dp) .height(50.dp) diff --git a/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewTest.kt b/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewTest.kt index 4ccf8c1..cc9cac4 100644 --- a/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewTest.kt +++ b/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewTest.kt @@ -21,8 +21,8 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import co.yml.coreui.core.ui.ytag.model.TagViewModifiers import co.yml.coreui.core.ui.ytag.TagView +import co.yml.coreui.core.ui.ytag.model.TagViewModifiers import org.junit.Rule import org.junit.Test @@ -50,7 +50,6 @@ class TagViewTest { @Test fun tagView_shown() { - launchYTag(text = "YTag") composeTestRule.onNodeWithTag("tag_view", useUnmergedTree = true).printToString() @@ -94,7 +93,6 @@ class TagViewTest { @Test fun tagView_leading_and_trailing_icon_shown() { - launchYTag(text = "YTag", leadingIcon = { IconButton(onClick = {}, modifier = Modifier.testTag("leading_icon")) { Icon( @@ -103,20 +101,20 @@ class TagViewTest { ) } }, trailingIcon = { - IconButton(onClick = {}, modifier = Modifier.testTag("trailing_icon")) { - Icon( - painter = painterResource(id = R.drawable.ic_menu_mylocation), - contentDescription = null - ) - } - }) + IconButton(onClick = {}, modifier = Modifier.testTag("trailing_icon")) { + Icon( + painter = painterResource(id = R.drawable.ic_menu_mylocation), + contentDescription = null + ) + } + }) composeTestRule.onNodeWithTag("leading_icon", useUnmergedTree = true).assertIsDisplayed() composeTestRule.onNodeWithTag("trailing_icon", useUnmergedTree = true).assertIsDisplayed() } @Test - fun tag_with_modifiers_are_executed(){ + fun tag_with_modifiers_are_executed() { val tagViewModifiers = TagViewModifiers.Builder() .minWidth(32.dp) .minHeight(100.dp) @@ -134,7 +132,7 @@ class TagViewTest { .overFlow(TextOverflow.Ellipsis) .softWrap(true) .maxLines(1) - .onTextLayout { } + .onTextLayout { } .style(TextStyle()) .enableBorder(true) .borderColor(Color.Red) @@ -145,7 +143,7 @@ class TagViewTest { .tonalElevation(2.dp) .shadowElevation(2.dp) .containerPaddingValues(PaddingValues(4.dp)) - .onCLick { } + .onCLick { } .borderWidth(1.dp) .build() diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/templates/AppBar.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/templates/AppBar.kt index 1b04dbf..0d1fe9e 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/templates/AppBar.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/templates/AppBar.kt @@ -38,13 +38,13 @@ fun AppBar() { } } - /** * Top app bars display information and actions at the top of a screen. * * @param title title of the screen * @param onBackPressed implementation of back press event */ +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AppBarWithBackButton(title: String, onBackPressed: () -> Unit) { Surface(shadowElevation = dimensionResource(id = R.dimen.padding_small)) { @@ -69,7 +69,7 @@ fun AppBarWithBackButton(title: String, onBackPressed: () -> Unit) { Icon( painter = painterResource(id = R.drawable.ic_back_arrow), contentDescription = "Back", - tint = Color.Black + tint = CoreUICatalogTheme.colors.primary ) } } @@ -77,30 +77,31 @@ fun AppBarWithBackButton(title: String, onBackPressed: () -> Unit) { } } - @Preview(showBackground = true) @Composable fun DefaultPreview() { CoreUICatalogTheme { - Scaffold(modifier = Modifier.fillMaxSize(), + Scaffold( + modifier = Modifier.fillMaxSize(), containerColor = CoreUICatalogTheme.colors.background, topBar = { AppBar() }, - content = { Box(Modifier.padding(it)) {} }) + content = { Box(Modifier.padding(it)) {} } + ) } } - @Preview(showBackground = true) @Composable fun AppBarWithBackButton() { CoreUICatalogTheme { - Scaffold(modifier = Modifier.fillMaxSize(), + Scaffold( + modifier = Modifier.fillMaxSize(), containerColor = CoreUICatalogTheme.colors.background, topBar = { AppBarWithBackButton(title = stringResource(id = R.string.title_y_tag)) { } }, - content = { Box(Modifier.padding(it)) {} }) - + content = { Box(Modifier.padding(it)) {} } + ) } } diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/theme/Shape.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/theme/Shape.kt index f01b1f0..f70fa87 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/theme/Shape.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/theme/Shape.kt @@ -10,4 +10,4 @@ data class CoreUICatalogShapes( val large: RoundedCornerShape = RoundedCornerShape(0.dp) ) -internal val LocalShapes = staticCompositionLocalOf {CoreUICatalogShapes() } +internal val LocalShapes = staticCompositionLocalOf { CoreUICatalogShapes() } diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagView.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagView.kt index 9f72f73..60c8b91 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagView.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagView.kt @@ -1,6 +1,11 @@ package co.yml.coreui.core.ui.ytag import android.R +import android.annotation.SuppressLint +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.* +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -11,8 +16,9 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.testTag @@ -20,11 +26,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension +import co.yml.coreui.core.ui.ytag.model.TagViewData import co.yml.coreui.core.ui.ytag.model.TagViewModifiers /** @@ -36,105 +42,152 @@ import co.yml.coreui.core.ui.ytag.model.TagViewModifiers * @param enabled controls the enabled state of the TagView * @param tagViewModifiers collection of modifier elements that decorate or add behavior to TagView elements */ +@SuppressLint("UnrememberedMutableState") @Composable fun TagView( text: String, - leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, - trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, + leadingIcon: @Composable ((tagViewData: TagViewData) -> Unit)? = null, + trailingIcon: @Composable ((tagViewData: TagViewData) -> Unit)? = null, enabled: Boolean = true, tagViewModifiers: TagViewModifiers = TagViewModifiers.Builder().build(), - overFlowText: String = "", - onClick: () -> Unit = {} + overFlowText: String = "" ) { + val tagViewData = TagViewData( + text = text, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + enabled = enabled, + tagViewModifiers = tagViewModifiers, + overFlowText = { overFlowText } + ) + + // used for visibility animation + val state = remember { + MutableTransitionState(false).apply { + targetState = true + } + } + + // used for alpha animation + var tagVisible by remember { + mutableStateOf(true) + } + + val tagAlpha: Float by animateFloatAsState( + targetValue = if (tagVisible) 1f else 0f, + animationSpec = tween( + durationMillis = tagViewModifiers.alphaAnimation.durationMillis, + easing = LinearEasing + ), + finishedListener = { + // delegate the click event once remove animation is completed + tagViewModifiers.onClick.invoke(tagViewData) + } + ) + with(tagViewModifiers) { - Surface( - shadowElevation = shadowElevation, - tonalElevation = tonalElevation, - shape = shape, - modifier = Modifier - .testTag("tag_view") - .width(width = width ?: Dp.Unspecified) - .height(height = height) + AnimatedVisibility( + visibleState = state, + enter = fadeIn(animationSpec = tween(alphaAnimation.durationMillis)), + exit = fadeOut(animationSpec = tween(alphaAnimation.durationMillis)) ) { - ConstraintLayout( + Surface( + shadowElevation = shadowElevation, + tonalElevation = tonalElevation, + shape = shape, modifier = Modifier - .width(width = width ?: Dp.Unspecified) - .height(height) - .run { - if (enableBorder) { - border( - width = borderWidth, - color = borderColor, - shape = shape - ) - } else { - background(color = backgroundColor, shape = shape) + .testTag("tag_view") + .width(width = width) + .height(height = height) + .alpha(tagAlpha) + ) { + ConstraintLayout( + modifier = Modifier + .width(width = width) + .height(height) + .run { + if (enableBorder) { + border( + width = borderWidth, + color = borderColor, + shape = shape + ) + } else { + background(color = backgroundColor, shape = shape) + } } - } - .clickable { - if (enabled) { - onClick.invoke() - tagViewModifiers.onClick.invoke() + .clickable { + if (enabled) { + if (tagViewModifiers.alphaAnimation.enabled) { + tagVisible = false + state.targetState = false + } else { + tagViewModifiers.onClick.invoke(tagViewData) + } + } } - } - .defaultMinSize(minWidth = minWidth, minHeight = minHeight) - .padding(containerPaddingValues) - .background( - color = backgroundColor, - shape = shape - ) - ) { - val (leading_icon, text_view, trailing_icon) = createRefs() + .defaultMinSize(minWidth = minWidth, minHeight = minHeight) + .padding(containerPaddingValues) + .background( + color = backgroundColor, + shape = shape + ) - Box(modifier = Modifier.constrainAs(leading_icon) { - start.linkTo(parent.start) - top.linkTo(parent.top) - bottom.linkTo(parent.bottom) - } ) { - leadingIcon?.invoke(enabled) - } + val (leading_icon, text_view, trailing_icon) = createRefs() - Text( - text = overFlowText.ifEmpty { text }, - color = textColor, - fontSize = fontSize, - fontWeight = fontWeight, - fontFamily = fontFamily, - fontStyle = fontStyle, - letterSpacing = letterSpacing, - modifier = Modifier - .constrainAs(text_view) { - start.linkTo(leading_icon.end) - end.linkTo(trailing_icon.start) + Box( + modifier = Modifier + .constrainAs(leading_icon) { + start.linkTo(parent.start) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + } + ) { + leadingIcon?.invoke(tagViewData) + } + + Text( + text = overFlowText.ifEmpty { text }, + color = textColor, + fontSize = fontSize, + fontWeight = fontWeight, + fontFamily = fontFamily, + fontStyle = fontStyle, + letterSpacing = letterSpacing, + modifier = Modifier + .constrainAs(text_view) { + start.linkTo(leading_icon.end) + end.linkTo(trailing_icon.start) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + width = Dimension.fillToConstraints + } + .padding( + textPadding + ) + .semantics { + this.contentDescription = semantics + }, + style = style, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + onTextLayout = onTextLayout + ) + Box( + modifier = Modifier.constrainAs(trailing_icon) { + end.linkTo(parent.end) top.linkTo(parent.top) bottom.linkTo(parent.bottom) - width = Dimension.fillToConstraints } - .padding( - textPadding - ) - .semantics { - this.contentDescription = semantics - }, - style = style, - textDecoration = textDecoration, - textAlign = textAlign, - lineHeight = lineHeight, - overflow = overflow, - softWrap = softWrap, - maxLines = maxLines, - onTextLayout = onTextLayout - ) - Box(modifier = Modifier.constrainAs(trailing_icon) { - end.linkTo(parent.end) - top.linkTo(parent.top) - bottom.linkTo(parent.bottom) - } - ) { - trailingIcon?.invoke(enabled) + ) { + trailingIcon?.invoke(tagViewData) + } } - } } } @@ -270,13 +323,13 @@ fun LeadingTrailingIconTag() { ) } }, trailingIcon = { - IconButton(onClick = {}) { - Icon( - painter = painterResource(id = R.drawable.ic_menu_close_clear_cancel), - contentDescription = null - ) - } - }, tagViewModifiers = tagViewModifiers) + IconButton(onClick = {}) { + Icon( + painter = painterResource(id = R.drawable.ic_menu_close_clear_cancel), + contentDescription = null + ) + } + }, tagViewModifiers = tagViewModifiers) } @Preview(name = "Tag with min width and height") diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagViewContainer.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagViewContainer.kt index 7cebc6e..c17a7b7 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagViewContainer.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagViewContainer.kt @@ -1,12 +1,18 @@ package co.yml.coreui.core.ui.ytag +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.key +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout @@ -33,7 +39,7 @@ fun TagViewContainer( tagViewData: List, tagViewContainerModifiers: TagViewContainerModifiers ) { - //add overflow details tag into the list + // add overflow details tag into the list val overFlowText = remember { mutableStateOf("") } @@ -43,86 +49,96 @@ fun TagViewContainer( overFlowText.value = moreTag.overFlowText.invoke(count) } - with(tagViewContainerModifiers) { - val context = LocalContext.current - var modifier = if (tagViewContainerModifiers.width != Dp.Unspecified) { - Modifier.width(tagViewContainerModifiers.width) - } else { - Modifier.wrapContentWidth() - } + if (tagViewData.isNotEmpty()) { + with(tagViewContainerModifiers) { + val context = LocalContext.current + var modifier = if (tagViewContainerModifiers.width != Dp.Unspecified) { + Modifier.width(tagViewContainerModifiers.width) + } else { + Modifier.wrapContentWidth() + } - modifier = if (tagViewContainerModifiers.height != Dp.Unspecified) { - modifier.then(Modifier.height(tagViewContainerModifiers.height)) - } else { - modifier.then(Modifier.wrapContentHeight()) - } + modifier = if (tagViewContainerModifiers.height != Dp.Unspecified) { + modifier.then(Modifier.height(tagViewContainerModifiers.height)) + } else { + modifier.then(Modifier.wrapContentHeight()) + } - modifier = modifier.then(Modifier - .run { - if (enableBorder) { - border( - width = borderWidth, - color = borderColor, + modifier = modifier.then( + Modifier + .run { + if (enableBorder) { + border( + width = borderWidth, + color = borderColor, + shape = shape + ) + } else { + background(color = backgroundColor, shape = shape) + } + } + .clickable { } + .semantics { + this.contentDescription = + tagViewContainerModifiers.semantics.ifEmpty { context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) } + } + .testTag("tag_view_container") + .background( + color = backgroundColor, shape = shape ) - } else { - background(color = backgroundColor, shape = shape) - } - } - .defaultMinSize(minWidth, minHeight) - .clickable { } - .semantics { - this.contentDescription = - tagViewContainerModifiers.semantics.ifEmpty { context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) } - } - .testTag("tag_view_container") - .background( - color = backgroundColor, - shape = shape + .padding(containerPaddingValues) + .animateContentSize( + animationSpec = tween( + durationMillis = 360, + easing = LinearEasing + ) + ) ) - .padding(containerPaddingValues) - ) - Box( - modifier = modifier - ) { - TagViewContainerLayout( - remainingTags = remainingTags, - tagViewContainerModifiers = tagViewContainerModifiers, - content = { - tagViewData.forEach { - with(it) { - val containerItemClick = { - tagViewContainerModifiers.onClick.invoke(it) + Box( + modifier = modifier + ) { + TagViewContainerLayout( + remainingTags = remainingTags, + tagViewContainerModifiers = tagViewContainerModifiers, + content = { + tagViewData.forEach { tagViewData -> + key(tagViewData) { + with(tagViewData) { + val tagViewModifiers = tagViewModifiers.copy(onClick = { + tagViewContainerModifiers.onClick.invoke(tagViewData) + }) + + TagView( + text = text, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + enabled = enabled, + tagViewModifiers = tagViewModifiers, + overFlowText = "" + ) + } } + } + + // over flow item + with(moreTag) { + val tagViewModifiers = tagViewModifiers.copy(onClick = { + tagViewContainerModifiers.onClick.invoke(moreTag) + }) TagView( - text = text, + text = overFlowText.value, leadingIcon = leadingIcon, trailingIcon = trailingIcon, enabled = enabled, tagViewModifiers = tagViewModifiers, - overFlowText = "", - onClick = containerItemClick + overFlowText = "" ) } } - - //over flow item - with(moreTag) { - val containerItemClick = { - tagViewContainerModifiers.onClick.invoke(this) - } - TagView( - text = overFlowText.value, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - enabled = enabled, - tagViewModifiers = tagViewModifiers, - overFlowText = "", - onClick = containerItemClick - ) - } - }) + ) + } } } } @@ -142,21 +158,17 @@ fun TagViewContainerLayout( val localDensity = LocalDensity.current Layout(content = content) { measurables, constraints -> - val looseConstraints = constraints.copy( - minWidth = 0, - minHeight = 0 - ) - var currentRow = 0 var currentOffset = IntOffset.Zero + val itemWidths = mutableListOf() - //Measurement phase + // Measurement phase val placeAbles = measurables.map { measurable -> - val placeAble: Placeable = measurable.measure(looseConstraints) + val placeAble: Placeable = measurable.measure(constraints) - //calculate the offsets to place the tags in layout phase + // calculate the offsets to place the tags in layout phase if (currentOffset.x > 0f && currentOffset.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() - .toInt() > constraints.maxWidth + .toInt() > constraints.maxWidth ) { currentRow += 1 currentOffset = @@ -167,85 +179,127 @@ fun TagViewContainerLayout( ) } placeAble to currentOffset.also { - currentOffset = it.copy( - x = it.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + val xOffset = + it.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() .toInt() + + itemWidths.add(IntOffset(xOffset, currentOffset.y + placeAble.height)) + + currentOffset = it.copy( + x = xOffset ) } } + itemWidths.removeAt(itemWidths.size - 1) + + val calculatedPlaceable = calculateTagPlacementOffset( + placeAbles = placeAbles, + tagViewContainerModifiers = tagViewContainerModifiers, + constraints = constraints, + localDensity = localDensity, + remainingTags = remainingTags + ) + + var width = 0 + var height = 0 + + if (calculatedPlaceable.isNotEmpty()) { + width = itemWidths.maxOf { it.x } + height = itemWidths.maxOf { it.y } + } + layout( - width = constraints.maxWidth, - height = constraints.maxHeight + width = if (tagViewContainerModifiers.width != Dp.Unspecified) constraints.maxWidth else width, + height = if (tagViewContainerModifiers.height != Dp.Unspecified) constraints.maxHeight else height ) { - placeAbles.forEachIndexed { index, tagPlaceable -> - if (index != placeAbles.lastIndex) { - val (placeable, offset) = tagPlaceable - //check whether container has enough space to place the current tag - if (offset.x + placeable.width < constraints.maxWidth && offset.y + placeable.height < constraints.maxHeight) { - //space available for current tag - val nextItemIndex = index + 1 - //check whether container has enough space to place the next tag - if (nextItemIndex <= placeAbles.lastIndex) { - val nextItemOffset = placeAbles[nextItemIndex].second - if (nextItemOffset.x + placeAbles[nextItemIndex].first.width < constraints.maxWidth && nextItemOffset.y + placeAbles[nextItemIndex].first.height < constraints.maxHeight) { - //space available for next tag - placeable.place(offset.x, offset.y) - } else { - //space not available for next tag - //place the over flow tag - //check whether to accommodate current tag and more - val moreTagPlaceAble = placeAbles.last() - val moreXOffset = - offset.x + placeable.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() - .toInt() - val moreYOffset = offset.y - if (moreXOffset + moreTagPlaceAble.first.width < constraints.maxWidth && - moreYOffset + moreTagPlaceAble.first.height < constraints.maxHeight - ) { - //place current tag - placeable.place(offset.x, offset.y) - //place more tag - val remainingItems = placeAbles.lastIndex - 1 - index - remainingTags.invoke(remainingItems) - moreTagPlaceAble.first.place(moreXOffset, moreYOffset) - return@layout - } else { - val overflow = showOverFlow( - index, - placeAbles, - tagViewContainerModifiers, - constraints, - localDensity, - remainingTags - ) - overflow?.let { - it.first.place(it.second) - } - return@layout - } - } - } + calculatedPlaceable.forEach { + it.first.place(it.second) + } + } + } +} + +fun calculateTagPlacementOffset( + placeAbles: List>, + tagViewContainerModifiers: TagViewContainerModifiers, + constraints: Constraints, + localDensity: Density, + remainingTags: (Int) -> Unit + +): List> { + val calculatedPlaceable = mutableListOf>() + + placeAbles.forEachIndexed { index, tagPlaceable -> + if (index != placeAbles.lastIndex) { + val (placeable, offset) = tagPlaceable + // check whether container has enough space to place the current tag + if (offset.x + placeable.width < constraints.maxWidth && offset.y + placeable.height < constraints.maxHeight) { + // space available for current tag + val nextItemIndex = index + 1 + // check whether container has enough space to place the next tag + if (nextItemIndex <= placeAbles.lastIndex) { + val nextItemOffset = placeAbles[nextItemIndex].second + if (nextItemOffset.x + placeAbles[nextItemIndex].first.width < constraints.maxWidth && nextItemOffset.y + placeAbles[nextItemIndex].first.height < constraints.maxHeight) { + // space available for next tag + calculatedPlaceable.add(Pair(placeable, offset)) } else { - //space not available for current tag - //place the over flow tag - val overflow = showOverFlow( - index, - placeAbles, - tagViewContainerModifiers, - constraints, - localDensity, - remainingTags - ) - overflow?.let { - it.first.place(it.second) + // space not available for next tag + // place the over flow tag + // check whether to accommodate current tag and more + val moreTagPlaceAble = placeAbles.last() + val moreXOffset = + offset.x + placeable.width + localDensity.run { tagViewContainerModifiers.tagSpacingHorizontal.toPx() } + .toInt() + val moreYOffset = offset.y + if (moreXOffset + moreTagPlaceAble.first.width < constraints.maxWidth && + moreYOffset + moreTagPlaceAble.first.height < constraints.maxHeight + ) { + // place current tag + calculatedPlaceable.add(Pair(placeable, offset)) + // place more tag + val remainingItems = placeAbles.lastIndex - 1 - index + remainingTags.invoke(remainingItems) + calculatedPlaceable.add( + Pair( + moreTagPlaceAble.first, + IntOffset(moreXOffset, moreYOffset) + ) + ) + } else { + val overflow = calculateOverFlowPlacement( + index, + placeAbles, + tagViewContainerModifiers, + constraints, + localDensity, + remainingTags + ) + overflow?.let { + calculatedPlaceable.add(Pair(it.first, it.second)) + } } - return@layout } } + } else { + // space not available for current tag + // place the over flow tag + val overflow = calculateOverFlowPlacement( + index, + placeAbles, + tagViewContainerModifiers, + constraints, + localDensity, + remainingTags + ) + overflow?.let { + calculatedPlaceable.add(Pair(it.first, it.second)) + } } } } + + return calculatedPlaceable } /** @@ -257,7 +311,7 @@ fun TagViewContainerLayout( * @param localDensity A density of the screen. Used for the conversions between pixels and Dp * @param remainingItems return item count which are not rendered in the tag view container */ -fun showOverFlow( +fun calculateOverFlowPlacement( index: Int, placeAbles: List>, tagViewContainerModifiers: TagViewContainerModifiers, @@ -270,8 +324,8 @@ fun showOverFlow( if (tagViewContainerModifiers.moreTagConfiguration.showOverFlow) { val moreTagPlaceAble = placeAbles.last() if (offset.x + moreTagPlaceAble.first.width < constraints.maxWidth && offset.y + moreTagPlaceAble.first.height < constraints.maxHeight) { - //place more tag - //check whether space available for over flow tag to place in between current [which replace over flow tag] and previous tags + // place more tag + // check whether space available for over flow tag to place in between current [which replace over flow tag] and previous tags val previousIndex = index - 1 if (previousIndex >= 0) { val previousOffset = placeAbles[previousIndex].second diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewContainerModifiers.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewContainerModifiers.kt index 778ffb8..b659173 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewContainerModifiers.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewContainerModifiers.kt @@ -8,7 +8,6 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -//todo sree_ is min width and min height required? /** * [TagViewContainerModifiers] Immutable collection of modifier elements that decorate or add behavior to TagView container. * @param minWidth define a default min width of TagViewContainer @@ -40,14 +39,14 @@ data class TagViewContainerModifiers( val tagSpacingVertical: Dp, val moreTagConfiguration: TagViewData, val onClick: (TagViewData) -> Unit, - val semantics: String + val semantics: String, + val alphaAnimation: AlphaAnimation ) { - //todo sree_ check min and max default size class Builder { private var minWidth: Dp = 150.dp private var minHeight: Dp = 150.dp private var width: Dp = Dp.Unspecified - private var height: Dp = Dp.Unspecified + private var height: Dp = Dp.Unspecified private var enableBorder: Boolean = false private var borderWidth: Dp = 1.dp private var borderColor: Color = Color.Black @@ -67,6 +66,7 @@ data class TagViewContainerModifiers( ) private var onClick: (TagViewData) -> Unit = {} private var semantics: String = "" + private var alphaAnimation: AlphaAnimation = AlphaAnimation() fun minWidth(minWidth: Dp) = apply { this.minWidth = minWidth } @@ -101,6 +101,8 @@ data class TagViewContainerModifiers( fun semantics(semantics: String) = apply { this.semantics = semantics } + fun alphaAnimation(alphaAnimation: AlphaAnimation) = apply { this.alphaAnimation = alphaAnimation } + fun build() = TagViewContainerModifiers( minWidth, minHeight, @@ -116,7 +118,8 @@ data class TagViewContainerModifiers( tagSpacingVertical, moreTagConfiguration, onClick, - semantics + semantics, + alphaAnimation ) } } diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewData.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewData.kt index e1953ae..04a4b43 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewData.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewData.kt @@ -16,9 +16,11 @@ import androidx.compose.runtime.Composable data class TagViewData( val text: String = "", val tagViewModifiers: TagViewModifiers = TagViewModifiers.Builder().build(), - val leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, - val trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, + val leadingIcon: @Composable ((tagViewData: TagViewData) -> Unit)? = null, + val trailingIcon: @Composable ((tagViewData: TagViewData) -> Unit)? = null, val enabled: Boolean = true, val showOverFlow: Boolean = true, - val overFlowText: (Int) -> String = { _ -> "" } + val overFlowText: (Int) -> String = { _ -> "" } ) + +data class AlphaAnimation(var enabled: Boolean = true, val durationMillis: Int = 650) diff --git a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewModifiers.kt b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewModifiers.kt index fa693ba..cce6127 100644 --- a/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewModifiers.kt +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewModifiers.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp - /** * [TagViewModifiers] Represents immutable collection of modifier elements that decorate or add behavior to TagView elements. * - If a parameter is explicitly set here then that parameter will always be used. @@ -55,7 +54,7 @@ import androidx.compose.ui.unit.sp data class TagViewModifiers( val minWidth: Dp, val minHeight: Dp, - val width: Dp?, + val width: Dp, val height: Dp, val textColor: Color, val fontSize: TextUnit, @@ -80,13 +79,15 @@ data class TagViewModifiers( val tonalElevation: Dp, val shadowElevation: Dp, val containerPaddingValues: PaddingValues, - val onClick: () -> Unit, - val semantics: String + val onClick: (TagViewData) -> Unit, + val semantics: String, + val alphaAnimation: AlphaAnimation + ) { class Builder { private var minWidth: Dp = 80.dp private var minHeight: Dp = 32.dp - private var width: Dp? = null + private var width: Dp = minWidth private var height: Dp = minHeight private var text: String = "" private var textColor: Color = Color.Black @@ -112,8 +113,9 @@ data class TagViewModifiers( private var tonalElevation: Dp = 0.dp private var shadowElevation: Dp = 0.dp private var containerPaddingValues: PaddingValues = PaddingValues(horizontal = 4.dp) - private var onClick: () -> Unit = {} + private var onClick: (TagViewData) -> Unit = {} private var semantics: String = text + private var alphaAnimation: AlphaAnimation = AlphaAnimation() fun minWidth(minWidth: Dp) = apply { this.minWidth = minWidth } @@ -168,9 +170,11 @@ data class TagViewModifiers( fun containerPaddingValues(paddingValues: PaddingValues) = apply { this.containerPaddingValues = paddingValues } - fun onCLick(onClick: () -> Unit) = apply { this.onClick = onClick } + fun onCLick(onClick: (TagViewData) -> Unit) = apply { this.onClick = onClick } fun semantics(semantics: String) = apply { this.semantics = semantics } + + fun alphaAnimation(alphaAnimation: AlphaAnimation) = apply { this.alphaAnimation = alphaAnimation } fun build() = TagViewModifiers( minWidth, minHeight, @@ -200,7 +204,8 @@ data class TagViewModifiers( shadowElevation, containerPaddingValues, onClick, - semantics + semantics, + alphaAnimation ) } } diff --git a/core/ui/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml index 00a3be5..6dbf1b0 100644 --- a/core/ui/src/main/res/values/colors.xml +++ b/core/ui/src/main/res/values/colors.xml @@ -7,7 +7,7 @@ #F9564F #89608E #E1F5FE - #01579B - #0288D1 + #81D4FA + #4FC3F7 diff --git a/feature/ytag/build.gradle.kts b/feature/ytag/build.gradle.kts index ceed718..a056709 100644 --- a/feature/ytag/build.gradle.kts +++ b/feature/ytag/build.gradle.kts @@ -32,4 +32,3 @@ dependencies { androidTestImplementation(versionCatalogLibs.androidx.compose.ui.test) androidTestImplementation(project(mapOf("path" to ":core:test"))) } - diff --git a/feature/ytag/src/main/java/co/yml/coreui/feature/ytag/ui/YTagActivity.kt b/feature/ytag/src/main/java/co/yml/coreui/feature/ytag/ui/YTagActivity.kt index 6bf076c..7fdefb3 100644 --- a/feature/ytag/src/main/java/co/yml/coreui/feature/ytag/ui/YTagActivity.kt +++ b/feature/ytag/src/main/java/co/yml/coreui/feature/ytag/ui/YTagActivity.kt @@ -2,19 +2,18 @@ package co.yml.coreui.feature.ytag.ui import android.content.Context import android.os.Bundle +import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Scaffold +import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -27,19 +26,20 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import co.yml.coreui.core.ui.templates.AppBarWithBackButton import co.yml.coreui.core.ui.theme.CoreUICatalogTheme -import co.yml.coreui.core.ui.ytag.TagView import co.yml.coreui.core.ui.ytag.TagViewContainer import co.yml.coreui.core.ui.ytag.model.TagViewContainerModifiers import co.yml.coreui.core.ui.ytag.model.TagViewData import co.yml.coreui.core.ui.ytag.model.TagViewModifiers import co.yml.coreui.ui.R import dagger.hilt.android.AndroidEntryPoint +import kotlin.random.Random @ExperimentalMaterial3Api @AndroidEntryPoint @@ -48,44 +48,24 @@ class YTagActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { CoreUICatalogTheme { - Scaffold(modifier = Modifier.fillMaxSize(), - containerColor = Color.White, - topBar = { - AppBarWithBackButton( - stringResource(id = R.string.title_y_tag), - onBackPressed = { - onBackPressed() - }) - }) - { - Box( + Scaffold(modifier = Modifier.fillMaxSize(), containerColor = CoreUICatalogTheme.colors.background, topBar = { + AppBarWithBackButton(stringResource(id = R.string.title_y_tag), + onBackPressed = { + onBackPressed() + }) + }) { + Column( modifier = Modifier .fillMaxSize() .background(Color.White) .padding(it) + .padding(PaddingValues(horizontal = dimensionResource(id = R.dimen.padding_normal))) ) { - LazyColumn( - content = { - items(10) { item -> - when (item) { - 0 -> CapsuleTag() - 1 -> RectangleTag() - 2 -> RoundRectangleTag() - 3 -> DefaultTag() - 4 -> TagWithLeadingIcon() - 5 -> TagWithTrailingIcon() - 6 -> TagWithLeadingTrailingIcon() - 7 -> BorderTag() - 8 -> ShadowTag() - 9 -> DefaultTagViewContainer() - } - } - }, - verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.padding_normal)), - modifier = Modifier - .padding(dimensionResource(id = R.dimen.padding_normal_medium)) - .align(Alignment.Center) - ) + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.padding_normal_medium))) + + CustomTagViewContainer() + + DefaultTagViewContainer() } } } @@ -94,221 +74,210 @@ class YTagActivity : ComponentActivity() { } val textStyle = TextStyle( - fontSize = 14.sp, - fontFamily = FontFamily.SansSerif, + fontSize = 14.sp, fontFamily = FontFamily.SansSerif ) -@Composable -fun DefaultTag() { - TagView(text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_default)) -} @Composable -fun CapsuleTag() { +fun DefaultTagViewContainer() { val context = LocalContext.current - val data = capsuleTagData( - context = context, - backgroundColor = colorResource(id = R.color.russian_violet) + val tagViewData = remember { + mutableStateListOf() + } + tagViewData.addAll( + (listOf( + capsuleTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_blue_300), + textColor = Color.Black + ), + + rectangleTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_blue_200), + textColor = Color.Black + ), + + roundRectTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_blue_300), + textColor = Color.Black + ), + + leadingIconTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_blue_200), + textColor = Color.Black, + iconTint = Color.Black + ), + + trailingIconData( + context = context, + backgroundColor = colorResource(id = R.color.light_blue_300), + textColor = Color.Black, + iconTint = Color.Black + ), + + leadingIconTrailingIconData( + context = context, + backgroundColor = colorResource(id = R.color.light_blue_200), + textColor = Color.Black, + iconTint = Color.Black + ) + )) ) - TagView( - text = data.text, - tagViewModifiers = data.tagViewModifiers - ) + DefaultTagViewContainer(tagViewData = tagViewData) } @Composable -fun RectangleTag() { +fun DefaultTagViewContainer(tagViewData: MutableList) { val context = LocalContext.current - val data = - rectangleTagData( - context = context, - backgroundColor = colorResource(id = R.color.light_green), - textColor = Color.Black - ) - TagView( - text = data.text, - tagViewModifiers = data.tagViewModifiers - ) -} + val tagViewContainerModifiers = + TagViewContainerModifiers.Builder().containerPaddingValues(PaddingValues(8.dp)) + .enableBorder(true).shape(RoundedCornerShape(4.dp)).tagSpacingVertical(8.dp) + .tagSpacingHorizontal(8.dp).backgroundColor(colorResource(id = R.color.cyan_50)) + .width(360.dp).height(50.dp).moreTagConfiguration( + TagViewData( + overFlowText = { count -> + "+ $count more" + }, + tagViewModifiers = TagViewModifiers.Builder() + .backgroundColor(colorResource(id = R.color.light_blue_300)) + .shape(CircleShape).width(80.dp).textAlign(TextAlign.Start).height(30.dp) + .maxLines(1).overFlow(TextOverflow.Ellipsis).textAlign(TextAlign.Center) + .textColor(Color.Black).fontWeight(FontWeight.Medium).onCLick { }.build() + ) + ).onCLick { item -> + // tag view item click + repeat(tagViewData.filter { it == item }.size) { + tagViewData.remove(item) + } -@Composable -fun RoundRectangleTag() { - val context = LocalContext.current - val data = roundRectTagData( - context = context, - backgroundColor = colorResource(id = R.color.light_yellow), - textColor = Color.Black - ) - TagView( - text = data.text, - tagViewModifiers = data.tagViewModifiers - ) -} + }.build() -@Composable -fun TagWithLeadingIcon() { - val context = LocalContext.current - val data = leadingIconTagData( - context = context, - backgroundColor = colorResource(id = R.color.bitter_sweet), - textColor = Color.White - ) - TagView( - text = data.text, - leadingIcon = data.leadingIcon, - tagViewModifiers = data.tagViewModifiers - ) -} + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Spacer(modifier = Modifier.height(32.dp)) -@Composable -fun TagWithTrailingIcon() { - val context = LocalContext.current - val data = trailingIconData( - context = context, - backgroundColor = colorResource(id = R.color.power), - textColor = Color.White - ) - TagView( - text = data.text, - trailingIcon = data.trailingIcon, - tagViewModifiers = data.tagViewModifiers - ) -} -@Composable -fun TagWithLeadingTrailingIcon() { - val context = LocalContext.current - val data = leadingIconTrailingIconData( - context = context, - backgroundColor = Color.Black, - textColor = Color.White - ) + TagViewContainer( + tagViewData = tagViewData, tagViewContainerModifiers = tagViewContainerModifiers + ) - TagView( - text = data.text, - leadingIcon = data.leadingIcon, - trailingIcon = data.trailingIcon, - tagViewModifiers = data.tagViewModifiers, - enabled = false - ) -} + Spacer(modifier = Modifier.height(16.dp)) -@Composable -fun BorderTag() { - val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) - .textColor(Color.Black) - .enableBorder(true) - .borderColor(Color.Red) - .borderWidth(dimensionResource(id = R.dimen.padding_very_tiny)) - .backgroundColor(Color.White) - .shape(CircleShape) - .style(textStyle) - .build() + val tagColor = colorResource(id = R.color.light_blue_200) + Button( + onClick = { + val tagData = TagViewData( + text = "${context.getString(co.yml.coreui.feature.ytag.R.string.tag_capsule)} ${ + Random.nextInt( + 1, 20 + ) + }", + tagViewModifiers = TagViewModifiers.Builder().width(90.dp).shape(CircleShape) + .backgroundColor(tagColor).textColor(Color.Black).style(textStyle).build() + ) + tagViewData.add(tagData) + }, modifier = Modifier + .fillMaxWidth() + .height(42.dp) + ) { + Text(text = "Add Tag View") + } - TagView( - text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_border), - tagViewModifiers = tagViewModifiers - ) + } } @Composable -fun ShadowTag() { - val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) - .textColor(colorResource(id = co.yml.coreui.feature.ytag.R.color.tag_text_color)) - .backgroundColor(colorResource(id = co.yml.coreui.feature.ytag.R.color.tag_background_color)) - .shape(CircleShape) - .shadowElevation(dimensionResource(id = R.dimen.padding_tiny)) - .style(textStyle) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .build() - - TagView( - text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_shadow), - tagViewModifiers = tagViewModifiers - ) -} +fun CustomTagViewContainer() { -@Composable -fun DefaultTagViewContainer() { val context = LocalContext.current - - val tagViewData = listOf( - - capsuleTagData(context = context, backgroundColor = colorResource(id = R.color.cyan_900)), - - rectangleTagData( - context = context, - backgroundColor = colorResource(id = R.color.cyan_700), - textColor = Color.White - ), - - roundRectTagData( - context = context, - backgroundColor = colorResource(id = R.color.cyan_900), - textColor = Color.White - ), - - leadingIconTagData( - context = context, - backgroundColor = colorResource(id = R.color.cyan_700), - textColor = Color.White - ), - - trailingIconData( - context = context, - backgroundColor = colorResource(id = R.color.cyan_900), - textColor = Color.White - ), - - leadingIconTrailingIconData( - context = context, - backgroundColor = colorResource(id = R.color.cyan_700), - textColor = Color.White - ) + val tagViewData = mutableListOf() + + tagViewData.addAll( + (listOf( + capsuleTagData( + context = context, + backgroundColor = colorResource(id = R.color.russian_violet), + textColor = Color.White + ), + + rectangleTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_green), + textColor = Color.Black + ), + + roundRectTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_yellow), + textColor = Color.Black + ), + TagViewData(text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_default)), + + leadingIconTagData( + context = context, + backgroundColor = colorResource(id = R.color.bitter_sweet), + textColor = Color.White, + iconTint = Color.White + ), + + trailingIconData( + context = context, + backgroundColor = colorResource(id = R.color.power), + textColor = Color.White, + iconTint = Color.White + ), + + leadingIconTrailingIconData( + context = context, backgroundColor = Color.Black, textColor = Color.White + ), + + borderTagData( + context = context, + backgroundColor = Color.White, + textColor = Color.Black + ), + + shadowTagData( + context = context, + backgroundColor = colorResource(id = co.yml.coreui.feature.ytag.R.color.tag_background_color), + textColor = colorResource(id = co.yml.coreui.feature.ytag.R.color.tag_text_color) + ) + )) ) - val tagViewContainerModifiers = TagViewContainerModifiers.Builder() - .containerPaddingValues(PaddingValues(8.dp)) - .enableBorder(true) - .shape(RoundedCornerShape(4.dp)) - .tagSpacingVertical(8.dp) - .tagSpacingHorizontal(8.dp) - .backgroundColor(colorResource(id = R.color.cyan_50)) - .width(260.dp) - .height(180.dp) - .moreTagConfiguration( - TagViewData( - overFlowText = { count -> - "$count more" - }, - tagViewModifiers = TagViewModifiers.Builder() - .backgroundColor(colorResource(id = R.color.cyan_50)) - .width(80.dp) - .textAlign(TextAlign.Start) - .height(30.dp) - .textColor(Color.Black) - .onCLick { }.build() - ) - ) - .onCLick { item -> - val itemIndex = tagViewData.indexOf(item) - val updatedList = tagViewData.toMutableList() - if (itemIndex != -1) { - updatedList.removeAt(itemIndex) - } - } - .build() + CustomTagViewContainer(tagViewData = tagViewData) +} + +@Composable +fun CustomTagViewContainer(tagViewData: MutableList) { + val tagViewContainerModifiers = + TagViewContainerModifiers.Builder().containerPaddingValues(PaddingValues(4.dp)) + .shape(RoundedCornerShape(4.dp)) + .tagSpacingVertical(dimensionResource(id = R.dimen.padding_normal)) + .tagSpacingHorizontal(dimensionResource(id = R.dimen.padding_normal)) + .moreTagConfiguration( + TagViewData( + overFlowText = { count -> + "+ $count more" + }, + tagViewModifiers = TagViewModifiers.Builder() + .backgroundColor(colorResource(id = R.color.light_blue_300)) + .shape(CircleShape).width(80.dp).textAlign(TextAlign.Start).height(30.dp) + .maxLines(1).overFlow(TextOverflow.Ellipsis).textAlign(TextAlign.Center) + .textColor(Color.Black).fontWeight(FontWeight.Medium).onCLick { }.build() + ) + ).onCLick { item -> + + }.build() TagViewContainer( - tagViewData = tagViewData, - tagViewContainerModifiers = tagViewContainerModifiers + tagViewData = tagViewData, tagViewContainerModifiers = tagViewContainerModifiers ) } @@ -316,15 +285,11 @@ fun DefaultTagViewContainer() { * @param context current context * @param backgroundColor tag view background color */ -fun capsuleTagData(context: Context, backgroundColor: Color): TagViewData { +fun capsuleTagData(context: Context, backgroundColor: Color, textColor: Color): TagViewData { return TagViewData( text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_capsule), - tagViewModifiers = TagViewModifiers.Builder() - .shape(CircleShape) - .backgroundColor(backgroundColor) - .textColor(Color.White) - .style(textStyle) - .build() + tagViewModifiers = TagViewModifiers.Builder().width(90.dp).shape(CircleShape) + .backgroundColor(backgroundColor).textColor(textColor).style(textStyle).build() ) } @@ -336,13 +301,8 @@ fun capsuleTagData(context: Context, backgroundColor: Color): TagViewData { fun rectangleTagData(context: Context, backgroundColor: Color, textColor: Color): TagViewData { return TagViewData( text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_rectangle), - tagViewModifiers = TagViewModifiers.Builder() - .width(90.dp) - .shape(RectangleShape) - .backgroundColor(backgroundColor) - .textColor(textColor) - .style(textStyle) - .build() + tagViewModifiers = TagViewModifiers.Builder().width(90.dp).shape(RectangleShape) + .backgroundColor(backgroundColor).textColor(textColor).style(textStyle).build() ) } @@ -356,10 +316,7 @@ fun roundRectTagData(context: Context, backgroundColor: Color, textColor: Color) text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_round_rectangle), tagViewModifiers = TagViewModifiers.Builder() .shape(RoundedCornerShape(context.resources.getDimension(R.dimen.padding_small))) - .width(120.dp) - .backgroundColor(backgroundColor) - .textColor(textColor) - .style(textStyle) + .width(140.dp).backgroundColor(backgroundColor).textColor(textColor).style(textStyle) .build() ) } @@ -369,33 +326,26 @@ fun roundRectTagData(context: Context, backgroundColor: Color, textColor: Color) * @param backgroundColor tag view background color * @param textColor tag view text color */ -fun leadingIconTagData(context: Context, backgroundColor: Color, textColor: Color): TagViewData { +fun leadingIconTagData( + context: Context, backgroundColor: Color, textColor: Color, iconTint: Color = Color.White +): TagViewData { return TagViewData(text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_leading_icon), - tagViewModifiers = TagViewModifiers.Builder() - .width(120.dp) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(backgroundColor) - .textColor(textColor) - .fontStyle(FontStyle.Italic) - .build(), - leadingIcon = { enabled -> - IconButton( - modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), + tagViewModifiers = TagViewModifiers.Builder().width(120.dp).maxLines(1) + .overFlow(TextOverflow.Ellipsis).shape(CircleShape).backgroundColor(backgroundColor) + .textColor(textColor).fontStyle(FontStyle.Italic).build(), + leadingIcon = { tagViewData -> + IconButton(modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), onClick = { - if (enabled) { + if (tagViewData.enabled) { Toast.makeText( - context, - context.getString(co.yml.coreui.feature.ytag.R.string.tag_leading_icon), - Toast.LENGTH_SHORT + context, tagViewData.text, Toast.LENGTH_SHORT ).show() } }) { Icon( painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), contentDescription = null, - tint = Color.White + tint = iconTint ) } }) @@ -406,34 +356,28 @@ fun leadingIconTagData(context: Context, backgroundColor: Color, textColor: Colo * @param backgroundColor tag view background color * @param textColor tag view text color */ -fun trailingIconData(context: Context, backgroundColor: Color, textColor: Color): TagViewData { +@Composable +fun trailingIconData( + context: Context, backgroundColor: Color, textColor: Color, iconTint: Color = Color.White +): TagViewData { return TagViewData(text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_trailing_icon), - tagViewModifiers = TagViewModifiers.Builder() - .width(150.dp) - .maxLines(1) - .textAlign(TextAlign.Start) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(backgroundColor) - .textColor(textColor) - .fontSize(15.sp) - .build(), - trailingIcon = { enabled -> + tagViewModifiers = TagViewModifiers.Builder().width(150.dp).maxLines(1) + .textAlign(TextAlign.Start).overFlow(TextOverflow.Ellipsis).shape(CircleShape) + .backgroundColor(backgroundColor).textColor(textColor).fontSize(15.sp).build(), + trailingIcon = { tagViewData -> IconButton(modifier = Modifier .padding(end = dimensionResource(id = R.dimen.padding_medium)) .size(dimensionResource(id = R.dimen.padding_normal_medium)), onClick = { - if (enabled) { + if (tagViewData.enabled) { Toast.makeText( - context, - context.getString(co.yml.coreui.feature.ytag.R.string.tag_trailing_icon), - Toast.LENGTH_SHORT + context, tagViewData.text, Toast.LENGTH_SHORT ).show() } }) { Icon( painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_close_20px), contentDescription = null, - tint = Color.White + tint = iconTint ) } }) @@ -445,54 +389,77 @@ fun trailingIconData(context: Context, backgroundColor: Color, textColor: Color) * @param textColor tag view text color */ fun leadingIconTrailingIconData( - context: Context, - backgroundColor: Color, - textColor: Color + context: Context, backgroundColor: Color, textColor: Color, iconTint: Color = Color.White ): TagViewData { return TagViewData(text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_leading_trailing_icon), - tagViewModifiers = TagViewModifiers.Builder() - .width(140.dp) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(backgroundColor) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .textColor(textColor) - .onCLick { - } - .build(), - leadingIcon = { enabled -> - val text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_leading_icon) - IconButton( - modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), + tagViewModifiers = TagViewModifiers.Builder().width(140.dp).maxLines(1) + .overFlow(TextOverflow.Ellipsis).shape(CircleShape).backgroundColor(backgroundColor) + .maxLines(1).overFlow(TextOverflow.Ellipsis).textColor(textColor).onCLick {}.build(), + leadingIcon = { tagViewData -> + IconButton(modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), onClick = { - if (enabled) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + if (tagViewData.enabled) { + Toast.makeText(context, tagViewData.text, Toast.LENGTH_SHORT).show() } }) { Icon( painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), contentDescription = null, - tint = Color.White + tint = iconTint ) } }, - trailingIcon = { enabled -> - val text = - stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_trailing_icon) + trailingIcon = { tagViewData -> IconButton(modifier = Modifier .padding(end = dimensionResource(id = R.dimen.padding_medium)) .size(dimensionResource(id = R.dimen.padding_normal_small)), onClick = { - if (enabled) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + if (tagViewData.enabled) { + Toast.makeText(context, tagViewData.text, Toast.LENGTH_SHORT).show() } }) { Icon( painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_close_20px), contentDescription = null, - tint = Color.White + tint = iconTint ) } }) } + +@Composable +fun borderTagData( + context: Context, + backgroundColor: Color, + textColor: Color +): TagViewData { + val tagViewModifiers = TagViewModifiers.Builder().width(100.dp).textColor(textColor) + .enableBorder(true) + .backgroundColor(backgroundColor).shape(CircleShape) + .borderColor(Color.Red) + .borderWidth(dimensionResource(id = R.dimen.padding_very_tiny)) + .style(textStyle).maxLines(1) + .overFlow(TextOverflow.Ellipsis).build() + + return TagViewData( + text = context.getString(co.yml.coreui.feature.ytag.R.string.tag_border), + tagViewModifiers = tagViewModifiers + ) +} + +@Composable +fun shadowTagData( + context: Context, + backgroundColor: Color, + textColor: Color, +): TagViewData { + val tagViewModifiers = TagViewModifiers.Builder().width(100.dp) + .textColor(textColor) + .backgroundColor(backgroundColor) + .shape(CircleShape).shadowElevation(dimensionResource(id = R.dimen.padding_tiny)) + .style(textStyle).maxLines(1).overFlow(TextOverflow.Ellipsis).build() + + return TagViewData( + text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_shadow), + tagViewModifiers = tagViewModifiers + ) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5296813..bbaf750 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,4 +30,3 @@ include(":core:data") include(":core:test") include("feature:ytag") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") -