From 9212b8cbf12a659ea400b8e44c84a54fbc226d4e Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Sat, 8 Apr 2023 22:54:10 +0530 Subject: [PATCH 1/8] 1. Added TagViewModifiers.kt modifier class 2. Added TagViewContainer custom layout 3. Added more tag when there is no space available. 4. Code refactoring. --- core/test/src/main/AndroidManifest.xml | 12 - core/ui/src/main/AndroidManifest.xml | 5 +- .../coreui/core/ui/ytag/TagViewContainer.kt | 245 ++++++++++++++++++ .../ytag/model/TagViewContainerModifiers.kt | 98 +++++++ .../coreui/core/ui/ytag/model/TagViewData.kt | 20 ++ .../core/ui/ytag/model/TagViewModifiers.kt | 2 +- .../coreui/feature/ytag/ui/YTagActivity.kt | 43 ++- 7 files changed, 405 insertions(+), 20 deletions(-) create mode 100644 core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagViewContainer.kt create mode 100644 core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewContainerModifiers.kt create mode 100644 core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewData.kt diff --git a/core/test/src/main/AndroidManifest.xml b/core/test/src/main/AndroidManifest.xml index cc58fed..9a40236 100644 --- a/core/test/src/main/AndroidManifest.xml +++ b/core/test/src/main/AndroidManifest.xml @@ -1,15 +1,3 @@ - - - - - - - - - - diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/main/AndroidManifest.xml index a5b3cd9..9a40236 100644 --- a/core/ui/src/main/AndroidManifest.xml +++ b/core/ui/src/main/AndroidManifest.xml @@ -1,6 +1,3 @@ - - - - \ No newline at end of file + 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 new file mode 100644 index 0000000..9b3a1c6 --- /dev/null +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/TagViewContainer.kt @@ -0,0 +1,245 @@ +package co.yml.coreui.core.ui.ytag + +import android.util.Log +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.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +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 + +/** + * [TagViewContainer] compose method used for hosting multiple chips + * + * @param tagViewData Defines the list of tag view data + * @param tagViewContainerModifiers collection of modifier elements that decorate or add behavior to TagView elements + */ +@Composable +fun TagViewContainer( + tagViewData: List, + tagViewContainerModifiers: TagViewContainerModifiers +) { + val updatedTagViewData = tagViewData.toMutableList() + val moreTagModifier = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .build() + + updatedTagViewData.add(TagViewData("More", moreTagModifier)) + + with(tagViewContainerModifiers) { + val modifier = Modifier + Box( + modifier = modifier + .width(width = width ?: Dp.Unspecified) + .height(height = height) + .defaultMinSize(minWidth = minWidth, minHeight = minHeight) + .run { + if (enableBorder) { + border( + width = borderWidth, + color = borderColor, + shape = shape + ) + } else { + background(color = backgroundColor, shape = shape) + } + } + .clickable { } + .testTag("tag_view_container") + .padding(containerPaddingValues) + .background( + color = backgroundColor, + shape = shape + ) + ) { + TagViewContainerLayout( + tagViewContainerModifiers = tagViewContainerModifiers, + content = { + updatedTagViewData.forEach { + with(it) { + TagView( + text = text, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + enabled = enabled, + tagViewModifiers = tagViewModifiers + ) + } + } + }) + } + } +} + +/** + * [TagViewContainerLayout] used for creating a custom layout to hosting y tag + * @param tagViewContainerModifiers collection of modifier elements that decorate or add behavior to tag view container + * @param content content of the container [Tag views] + */ +@Composable +fun TagViewContainerLayout( + tagViewContainerModifiers: TagViewContainerModifiers, + content: @Composable () -> Unit +) { + Layout(content = content) { measurables, constraints -> + val looseConstraints = constraints.copy( + minWidth = 0, + minHeight = 0 + ) + + var currentRow = 0 + var currentOffset = IntOffset.Zero + + //todo sree_ check whether the padding is correct + + val placeAbles = measurables.map { measurable -> + val placeAble = measurable.measure(looseConstraints) + + //todo sree_ is horizontal and vertical space [surface] required + if (currentOffset.x > 0f && currentOffset.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + .toInt() > constraints.maxWidth + ) { + Log.i( + "check_measure", + "c.XOffset: ${currentOffset.x} p.Width: ${placeAble.width} p.Height: ${placeAble.height} tagHSpace: ${ + tagViewContainerModifiers.tagSpacingHorizontal.toPx().toInt() + } tagVSpace: ${ + tagViewContainerModifiers.tagSpacingVertical.toPx().toInt() + } max w: ${constraints.maxWidth}" + ) + currentRow += 1 + currentOffset = + currentOffset.copy( + x = 0, + y = currentOffset.y + placeAble.height + tagViewContainerModifiers.tagSpacingVertical.toPx() + .toInt() + ) + } + Log.i("check_measure", "common: ${currentOffset}") + + placeAble to currentOffset.also { + currentOffset = it.copy( + x = it.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + .toInt() + ) + } + } + + layout(width = constraints.maxWidth, + height = placeAbles.lastOrNull()?.run { first.height } ?: 0) { + Log.i("check_placeAble", "size: ${placeAbles.size}") + placeAbles.forEachIndexed { index, placeable -> + val (placeable, offset) = placeable + //check whether current item has enough space + val currentItemHorizontalSpace = + placeable.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + .toInt() + val currentItemVerticalSpace = + placeable.height + tagViewContainerModifiers.tagSpacingVertical.toPx() + .toInt() + + if (offset.x + currentItemHorizontalSpace < constraints.maxWidth && offset.y + currentItemVerticalSpace < constraints.maxHeight) { + //current item has space + Log.i("check_placeable", "index: $index current item has space") + val nextItemIndex = index + 1 + if (nextItemIndex <= placeAbles.lastIndex) { + val nextItemOffset = placeAbles[nextItemIndex].second + + val nextItemHorizontalSpace = + placeAbles[nextItemIndex].first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + .toInt() + val nextItemVerticalSpace = + placeAbles[nextItemIndex].first.height + tagViewContainerModifiers.tagSpacingVertical.toPx() + .toInt() + + if (nextItemOffset.x + nextItemHorizontalSpace < constraints.maxWidth && nextItemOffset.y + nextItemVerticalSpace < constraints.maxHeight) { + //next item has space + Log.i( + "check_placeable", + "next index: $nextItemIndex nextItemOffset : $nextItemOffset next item has space and placed" + ) + placeable.place(offset.x, offset.y) + } else { + //next item has no space + //check space to accommodate more tag + Log.i("check_placeable", "index: $index next item has no space") + val moreTagPlaceAble = placeAbles.last() + val moreTagHorizontalSpace = + moreTagPlaceAble.first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + .toInt() + val moreTagVerticalSpace = + moreTagPlaceAble.first.height + tagViewContainerModifiers.tagSpacingVertical.toPx() + .toInt() + + if (offset.x + moreTagHorizontalSpace < constraints.maxWidth && offset.y + moreTagVerticalSpace < constraints.maxHeight) { + //place more tag + Log.i("check_placeable", "index: $index more tag placed") + moreTagPlaceAble.first.place(offset.x, offset.y) + return@layout + } else { + Log.i("check_placeable", "index: $index more tag has no space") + } + } + } + } else { + //current item has no space + //check space to accommodate more tag + Log.i("check_placeable", "index: $index current item has no space") + val moreTagPlaceAble = placeAbles.last() + val moreTagHorizontalSpace = + moreTagPlaceAble.first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() + .toInt() + val moreTagVerticalSpace = + moreTagPlaceAble.first.height + tagViewContainerModifiers.tagSpacingVertical.toPx() + .toInt() + + if (offset.x + moreTagHorizontalSpace < constraints.maxWidth && offset.y + moreTagVerticalSpace < constraints.maxHeight) { + //place more tag + Log.i("check_placeable", "index: $index more tag placed") + placeable.place(offset.x, offset.y) + return@layout + } else { + Log.i("check_placeable", "index: $index more tag has no space") + } + } + } + } + } +} + +@Preview(name = "Default Tag container") +@Composable +fun DefaultTagContainer() { + val tagViewModifiers = TagViewModifiers.Builder() + .build() + val tagViewData = listOf( + TagViewData(text = "Tag 1", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 2", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 3", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 4", tagViewModifiers = tagViewModifiers) + ) + + val tagViewContainerModifiers = TagViewContainerModifiers.Builder() + .shape(RoundedCornerShape(10.dp)) + .backgroundColor(Color.Gray) + .build() + + TagViewContainer( + tagViewData = tagViewData, + tagViewContainerModifiers = tagViewContainerModifiers + ) +} 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 new file mode 100644 index 0000000..c346fd7 --- /dev/null +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewContainerModifiers.kt @@ -0,0 +1,98 @@ +package co.yml.coreui.core.ui.ytag.model + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +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 + * @param minHeight define a default min height of TagViewContainer + * @param width define width of TagViewContainer + * @param height define height of TagViewContainer + * @param enableBorder enable border for TagViewContainer + * @param borderWidth define borderWidth of the TagViewContainer + * @param borderColor define borderColor of the TagViewContainer + * @param backgroundColor define backgroundColor of the TagViewContainer + * @param shape defines the shape of the TagViewContainer + * @param containerPaddingValues define padding for TagViewContainer + * @param tagSpacingHorizontal horizontal padding between tag views + * @param tagSpacingVertical vertical padding between tag views + * + */ +data class TagViewContainerModifiers( + val minWidth: Dp, + val minHeight: Dp, + val width: Dp?, + val height: Dp, + val enableBorder: Boolean, + val borderWidth: Dp, + val borderColor: Color, + val backgroundColor: Color, + val shape: Shape, + val containerPaddingValues: PaddingValues, + val tagSpacingHorizontal: Dp, + val tagSpacingVertical: Dp +) { + //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? = null + private var height: Dp = minHeight + private var enableBorder: Boolean = false + private var borderWidth: Dp = 1.dp + private var borderColor: Color = Color.Black + private var backgroundColor: Color = Color.White + private var shape: Shape = RectangleShape + private var containerPaddingValues: PaddingValues = PaddingValues(horizontal = 4.dp, vertical = 4.dp) + private var tagSpacingHorizontal: Dp = 8.dp + private var tagSpacingVertical: Dp = 8.dp + + + fun minWidth(minWidth: Dp) = apply { this.minWidth = minWidth } + + fun minHeight(minHeight: Dp) = apply { this.minHeight = minHeight } + + fun width(width: Dp) = apply { this.width = width } + + fun height(height: Dp) = apply { this.height = height } + + fun enableBorder(enableBorder: Boolean) = apply { this.enableBorder = enableBorder } + + fun borderWidth(borderWidth: Dp) = apply { this.borderWidth = borderWidth } + + fun borderColor(borderColor: Color) = apply { this.borderColor = borderColor } + + fun backgroundColor(backgroundColor: Color) = + apply { this.backgroundColor = backgroundColor } + + fun shape(shape: Shape) = apply { this.shape = shape } + + fun containerPaddingValues(paddingValues: PaddingValues) = + apply { this.containerPaddingValues = paddingValues } + + fun tagSpacingHorizontal(space: Dp) = apply { this.tagSpacingHorizontal = space } + + fun tagSpacingVertical(space: Dp) = apply { this.tagSpacingVertical = space } + + fun build() = TagViewContainerModifiers( + minWidth, + minHeight, + width, + height, + enableBorder, + borderWidth, + borderColor, + backgroundColor, + shape, + containerPaddingValues, + tagSpacingHorizontal, + tagSpacingVertical + ) + } +} 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 new file mode 100644 index 0000000..1467822 --- /dev/null +++ b/core/ui/src/main/java/co/yml/coreui/core/ui/ytag/model/TagViewData.kt @@ -0,0 +1,20 @@ +package co.yml.coreui.core.ui.ytag.model + +import androidx.compose.runtime.Composable + +/** + * [TagViewData] Used for holding the TagView data + * + * @param text text the text to be displayed + * @param leadingIcon the optional leading icon to be displayed at the beginning of the TagView + * @param trailingIcon the optional leading icon to be displayed at the end of the TagView + * @param enabled controls the enabled state of the TagView + * @param tagViewModifiers collection of modifier elements that decorate or add behavior to TagView elements + */ +data class TagViewData( + val text: String, + val tagViewModifiers: TagViewModifiers, + val leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, + val trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, + val enabled: Boolean = true +) 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 e252d3c..4f9b96c 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 @@ -19,7 +19,7 @@ import androidx.compose.ui.unit.sp /** - * Immutable collection of modifier elements that decorate or add behavior to TagView elements. + * [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. * - If a parameter is not set then the corresponding default value will be used * 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 e0d8626..28f3d0a 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 @@ -27,12 +27,16 @@ 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.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.model.TagViewModifiers import co.yml.coreui.ui.R 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 dagger.hilt.android.AndroidEntryPoint @ExperimentalMaterial3Api @@ -61,17 +65,18 @@ class YTagActivity : ComponentActivity() { ) { LazyColumn( content = { - items(9) { item -> + items(10) { item -> when (item) { 0 -> CapsuleTag() - 1 -> RectangleTag() - 2 -> RoundRectangleTag() +// 1 -> RectangleTag() +// 2 -> RoundRectangleTag() 3 -> DefaultTag() 4 -> TagWithLeadingIcon() 5 -> TagWithTrailingIcon() 6 -> TagWithLeadingTrailingIcon() 7 -> BorderTag() 8 -> ShadowTag() + 9 -> DefaultTagViewContainer() } } }, @@ -284,3 +289,35 @@ fun ShadowTag() { tagViewModifiers = tagViewModifiers ) } + +@Composable +fun DefaultTagViewContainer(){ + val tagViewModifiers = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .style(textStyle) + .build() + + val tagViewData = listOf( + TagViewData("Tag view 1", tagViewModifiers), + TagViewData("Tag view 2", tagViewModifiers), + TagViewData("Tag view 3", tagViewModifiers), +// TagViewData("Tag view 4", tagViewModifiers), +// TagViewData("Tag view 5", tagViewModifiers), +// TagViewData("Tag view 6", tagViewModifiers), +// TagViewData("Tag view 7", tagViewModifiers) + ) + + val tagViewContainerModifiers = TagViewContainerModifiers.Builder() + .width(220.dp) + .height(100.dp) + .containerPaddingValues(PaddingValues(8.dp)) + .enableBorder(true) + .shape(RoundedCornerShape(4.dp)) + .tagSpacingVertical(16.dp) + .tagSpacingHorizontal(8.dp) + .build() + + TagViewContainer(tagViewData = tagViewData, tagViewContainerModifiers = tagViewContainerModifiers) +} From 62bdb8c30ce57406429aa22c607b553f3ba85740 Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Mon, 10 Apr 2023 10:51:48 +0530 Subject: [PATCH 2/8] 1. Added compose ui testing. 2. Tag view container action --- .../coreui/ui/ytag/TagViewContainerTesting.kt | 102 +++++++++++++++++ core/ui/src/main/AndroidManifest.xml | 3 + .../co/yml/coreui/core/ui/ytag/TagView.kt | 8 +- .../coreui/core/ui/ytag/TagViewContainer.kt | 108 +++++++++++++++--- .../ytag/model/TagViewContainerModifiers.kt | 27 ++++- .../coreui/core/ui/ytag/model/TagViewData.kt | 5 +- core/ui/src/main/res/values/strings.xml | 2 + .../coreui/feature/ytag/ui/YTagActivity.kt | 18 +++ 8 files changed, 249 insertions(+), 24 deletions(-) create mode 100644 core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewContainerTesting.kt 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 new file mode 100644 index 0000000..7cd908c --- /dev/null +++ b/core/ui/src/androidTest/java/co/yml/coreui/ui/ytag/TagViewContainerTesting.kt @@ -0,0 +1,102 @@ +package co.yml.coreui.ui.ytag + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.unit.dp +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 org.junit.Rule +import org.junit.Test + +class TagViewContainerTesting { + @get:Rule + val composeTestRule = createComposeRule() + + private fun launchTagViewContainer( + tagViewContainerModifiers: TagViewContainerModifiers = TagViewContainerModifiers.Builder() + .shape(RoundedCornerShape(10.dp)) + .width(200.dp) + .height(120.dp) + .build() + ) { + val tagViewModifiers = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .build() + + val tagViewData = listOf( + TagViewData(text = "Tag 1", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 2", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 3", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 4", tagViewModifiers = tagViewModifiers) + ) + + composeTestRule.setContent { + TagViewContainer( + tagViewData = tagViewData, + tagViewContainerModifiers = tagViewContainerModifiers + ) + } + } + + @Test + fun tagViewContainer_shown() { + launchTagViewContainer() + + println( + "tag_view_container ${composeTestRule.onNodeWithTag("tag_view_container", useUnmergedTree = true).printToString()}" + ) + + composeTestRule.onNodeWithTag("tag_view_container").assertIsDisplayed() + } + + @Test + fun tagViewContainer_with_modifiers_are_executed() { + val tagViewContainerModifiers = TagViewContainerModifiers.Builder() + .minWidth(150.dp) + .minHeight(150.dp) + .width(200.dp) + .height(150.dp) + .enableBorder(true) + .borderWidth(1.dp) + .borderColor(Color.Red) + .backgroundColor(Color.Gray) + .shape(CircleShape) + .containerPaddingValues(PaddingValues(8.dp)) + .tagSpacingHorizontal(8.dp) + .tagSpacingVertical(8.dp) + .moreTagConfiguration(TagViewData(text = "more")) + .build() + + launchTagViewContainer(tagViewContainerModifiers) + + composeTestRule.onNodeWithTag("tag_view_container") + .assertIsDisplayed() + } + + @Test + fun tagViewContainer_tags_shown(){ + launchTagViewContainer() + + composeTestRule.onNodeWithContentDescription("Tag 1").assertIsDisplayed() + } + + @Test + fun tagViewContainer_with_less_space_more_tag_shown(){ + val tagViewContainerModifiers = TagViewContainerModifiers.Builder() + .width(150.dp) + .height(50.dp) + .build() + + launchTagViewContainer(tagViewContainerModifiers) + + composeTestRule.onNodeWithContentDescription("more").assertIsDisplayed() + } +} diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/main/AndroidManifest.xml index 9a40236..7bc4648 100644 --- a/core/ui/src/main/AndroidManifest.xml +++ b/core/ui/src/main/AndroidManifest.xml @@ -1,3 +1,6 @@ + + + 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 7462276..1a0c428 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 @@ -21,6 +21,7 @@ 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 co.yml.coreui.core.ui.ytag.model.TagViewData import co.yml.coreui.core.ui.ytag.model.TagViewModifiers /** @@ -38,7 +39,9 @@ fun TagView( leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, enabled: Boolean = true, - tagViewModifiers: TagViewModifiers = TagViewModifiers.Builder().build() + tagViewModifiers: TagViewModifiers = TagViewModifiers.Builder().build(), + overFlowText: String = "", + onClick: () -> Unit = {} ) { with(tagViewModifiers) { Surface( @@ -66,6 +69,7 @@ fun TagView( .clickable { if (enabled) { onClick.invoke() + tagViewModifiers.onClick.invoke() } } .defaultMinSize(minWidth = minWidth, minHeight = minHeight) @@ -79,7 +83,7 @@ fun TagView( leadingIcon?.invoke(enabled) Text( - text = text, + text = overFlowText.ifEmpty { text }, color = textColor, fontSize = fontSize, fontWeight = fontWeight, 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 9b3a1c6..65f2198 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 @@ -11,7 +11,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag +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.IntOffset @@ -31,22 +35,16 @@ fun TagViewContainer( tagViewData: List, tagViewContainerModifiers: TagViewContainerModifiers ) { + //add more tag into the list val updatedTagViewData = tagViewData.toMutableList() - val moreTagModifier = TagViewModifiers.Builder() - .shape(CircleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .build() - - updatedTagViewData.add(TagViewData("More", moreTagModifier)) + val moreTag = tagViewContainerModifiers.moreTagConfiguration + updatedTagViewData.add(moreTag) with(tagViewContainerModifiers) { val modifier = Modifier + val context = LocalContext.current Box( modifier = modifier - .width(width = width ?: Dp.Unspecified) - .height(height = height) - .defaultMinSize(minWidth = minWidth, minHeight = minHeight) .run { if (enableBorder) { border( @@ -58,25 +56,39 @@ fun TagViewContainer( background(color = backgroundColor, shape = shape) } } + .width(width = width ?: Dp.Unspecified) + .height(height = height) + .defaultMinSize(minWidth = minWidth, minHeight = minHeight) .clickable { } + .semantics { + this.contentDescription = + context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) + } .testTag("tag_view_container") - .padding(containerPaddingValues) .background( color = backgroundColor, shape = shape ) + .padding(containerPaddingValues) ) { + + TagViewContainerLayout( tagViewContainerModifiers = tagViewContainerModifiers, content = { updatedTagViewData.forEach { with(it) { + val containerItemClick = { + tagViewContainerModifiers.onClick.invoke(it) + } TagView( text = text, leadingIcon = leadingIcon, trailingIcon = trailingIcon, enabled = enabled, - tagViewModifiers = tagViewModifiers + tagViewModifiers = tagViewModifiers, + overFlowText = "", + onClick = containerItemClick ) } } @@ -104,12 +116,9 @@ fun TagViewContainerLayout( var currentRow = 0 var currentOffset = IntOffset.Zero - //todo sree_ check whether the padding is correct - val placeAbles = measurables.map { measurable -> - val placeAble = measurable.measure(looseConstraints) + val placeAble: Placeable = measurable.measure(looseConstraints) - //todo sree_ is horizontal and vertical space [surface] required if (currentOffset.x > 0f && currentOffset.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() .toInt() > constraints.maxWidth ) { @@ -178,6 +187,10 @@ fun TagViewContainerLayout( //check space to accommodate more tag Log.i("check_placeable", "index: $index next item has no space") val moreTagPlaceAble = placeAbles.last() + val remainingTags = placeAbles.lastIndex - 1 - index + + +// tagViewContainerModifiers.moreTagConfiguration.overFlowText.invoke(remainingTags) val moreTagHorizontalSpace = moreTagPlaceAble.first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() .toInt() @@ -199,6 +212,7 @@ fun TagViewContainerLayout( //current item has no space //check space to accommodate more tag Log.i("check_placeable", "index: $index current item has no space") + val remainingTags = placeAbles.lastIndex - 1 - index val moreTagPlaceAble = placeAbles.last() val moreTagHorizontalSpace = moreTagPlaceAble.first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() @@ -225,6 +239,66 @@ fun TagViewContainerLayout( @Composable fun DefaultTagContainer() { val tagViewModifiers = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .build() + val tagViewData = listOf( + TagViewData(text = "Tag 1", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 2", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 3", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 4", tagViewModifiers = tagViewModifiers) + ) + + val tagViewContainerModifiers = TagViewContainerModifiers.Builder() + .shape(RoundedCornerShape(10.dp)) + .width(200.dp) + .height(120.dp) + .build() + + TagViewContainer( + tagViewData = tagViewData, + tagViewContainerModifiers = tagViewContainerModifiers + ) +} + +@Preview(name = "Tag container with border") +@Composable +fun BorderTagContainer() { + val tagViewModifiers = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .build() + val tagViewData = listOf( + TagViewData(text = "Tag 1", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 2", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 3", tagViewModifiers = tagViewModifiers), + TagViewData(text = "Tag 4", tagViewModifiers = tagViewModifiers) + ) + + val tagViewContainerModifiers = TagViewContainerModifiers.Builder() + .shape(RoundedCornerShape(10.dp)) + .width(200.dp) + .height(120.dp) + .enableBorder(true) + .borderColor(Color.Red) + .borderWidth(1.dp) + .build() + + TagViewContainer( + tagViewData = tagViewData, + tagViewContainerModifiers = tagViewContainerModifiers + ) +} + +@Preview(name = "Tag container with background") +@Composable +fun BackgroundTagContainer() { + val tagViewModifiers = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) .build() val tagViewData = listOf( TagViewData(text = "Tag 1", tagViewModifiers = tagViewModifiers), @@ -236,6 +310,8 @@ fun DefaultTagContainer() { val tagViewContainerModifiers = TagViewContainerModifiers.Builder() .shape(RoundedCornerShape(10.dp)) .backgroundColor(Color.Gray) + .width(200.dp) + .height(120.dp) .build() TagViewContainer( 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 c346fd7..b668bd2 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 @@ -1,6 +1,7 @@ package co.yml.coreui.core.ui.ytag.model import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.CircleShape import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape @@ -36,7 +37,9 @@ data class TagViewContainerModifiers( val shape: Shape, val containerPaddingValues: PaddingValues, val tagSpacingHorizontal: Dp, - val tagSpacingVertical: Dp + val tagSpacingVertical: Dp, + val moreTagConfiguration: TagViewData, + val onClick: (TagViewData) -> Unit ) { //todo sree_ check min and max default size class Builder { @@ -49,10 +52,19 @@ data class TagViewContainerModifiers( private var borderColor: Color = Color.Black private var backgroundColor: Color = Color.White private var shape: Shape = RectangleShape - private var containerPaddingValues: PaddingValues = PaddingValues(horizontal = 4.dp, vertical = 4.dp) + private var containerPaddingValues: PaddingValues = + PaddingValues(horizontal = 4.dp, vertical = 4.dp) private var tagSpacingHorizontal: Dp = 8.dp private var tagSpacingVertical: Dp = 8.dp - + private var moreTagConfiguration: TagViewData = TagViewData( + text = "more", + tagViewModifiers = TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .build() + ) + private var onClick: (TagViewData) -> Unit = {} fun minWidth(minWidth: Dp) = apply { this.minWidth = minWidth } @@ -80,6 +92,11 @@ data class TagViewContainerModifiers( fun tagSpacingVertical(space: Dp) = apply { this.tagSpacingVertical = space } + fun moreTagConfiguration(configuration: TagViewData) = + apply { this.moreTagConfiguration = configuration } + + fun onCLick(onClick: (TagViewData) -> Unit) = apply { this.onClick = onClick } + fun build() = TagViewContainerModifiers( minWidth, minHeight, @@ -92,7 +109,9 @@ data class TagViewContainerModifiers( shape, containerPaddingValues, tagSpacingHorizontal, - tagSpacingVertical + tagSpacingVertical, + moreTagConfiguration, + onClick ) } } 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 1467822..0b719e6 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 @@ -13,8 +13,9 @@ import androidx.compose.runtime.Composable */ data class TagViewData( val text: String, - val tagViewModifiers: TagViewModifiers, + val tagViewModifiers: TagViewModifiers = TagViewModifiers.Builder().build(), val leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, val trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, - val enabled: Boolean = true + val enabled: Boolean = true, + val overFlowText: (Int) -> String = { _ -> "" } ) diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index c51354a..5c92fc8 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -2,4 +2,6 @@ CoreUICatalogApp Tags + more + Tag view container 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 28f3d0a..5209bcd 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 @@ -1,6 +1,7 @@ package co.yml.coreui.feature.ytag.ui import android.os.Bundle +import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -297,6 +298,10 @@ fun DefaultTagViewContainer(){ .backgroundColor(Color.Black) .textColor(Color.White) .style(textStyle) + .onCLick { + Log.i("check_click","tag view clicked") + //remove the current tag view from the list + } .build() val tagViewData = listOf( @@ -317,6 +322,19 @@ fun DefaultTagViewContainer(){ .shape(RoundedCornerShape(4.dp)) .tagSpacingVertical(16.dp) .tagSpacingHorizontal(8.dp) + .moreTagConfiguration(TagViewData(text = "more ...", overFlowText = {count -> + "$count more items" + }, tagViewModifiers = TagViewModifiers.Builder().onCLick { Log.i("check_click","more tag clicked") }.build())) + .onCLick {item -> + val itemIndex = tagViewData.indexOf(item) + val updatedList = tagViewData.toMutableList() + + if (itemIndex!= -1){ + updatedList.removeAt(itemIndex) + } + + Log.i("check_click","tag view container item $itemIndex nam: ${item.text} clicked currentList size: ${tagViewData.size} updatedList size: ${updatedList.size}") + } .build() TagViewContainer(tagViewData = tagViewData, tagViewContainerModifiers = tagViewContainerModifiers) From 713e7f2801ecc6c163300b2b20844315f639dc4a Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Mon, 10 Apr 2023 16:53:06 +0530 Subject: [PATCH 3/8] 1. Added more tag display property 2. Code refactoring 3. Bug fixes [more tag position issue] --- .../coreui/core/ui/ytag/TagViewContainer.kt | 223 +++++++++++------- .../ytag/model/TagViewContainerModifiers.kt | 6 +- .../coreui/core/ui/ytag/model/TagViewData.kt | 1 + .../coreui/feature/ytag/ui/YTagActivity.kt | 59 +++-- 4 files changed, 178 insertions(+), 111 deletions(-) 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 65f2198..ccd6425 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 @@ -13,13 +13,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Placeable import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag 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.IntOffset -import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.* 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 @@ -41,38 +40,48 @@ fun TagViewContainer( updatedTagViewData.add(moreTag) with(tagViewContainerModifiers) { - val modifier = Modifier 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 = modifier.then(Modifier + .run { + if (enableBorder) { + border( + width = borderWidth, + color = borderColor, + shape = shape + ) + } else { + background(color = backgroundColor, shape = shape) + } + } + .defaultMinSize(minWidth, minHeight) + .clickable { } + .semantics { + this.contentDescription = + context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) + } + .testTag("tag_view_container") + .background( + color = backgroundColor, + shape = shape + ) + .padding(containerPaddingValues) + ) + Box( modifier = modifier - .run { - if (enableBorder) { - border( - width = borderWidth, - color = borderColor, - shape = shape - ) - } else { - background(color = backgroundColor, shape = shape) - } - } - .width(width = width ?: Dp.Unspecified) - .height(height = height) - .defaultMinSize(minWidth = minWidth, minHeight = minHeight) - .clickable { } - .semantics { - this.contentDescription = - context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) - } - .testTag("tag_view_container") - .background( - color = backgroundColor, - shape = shape - ) - .padding(containerPaddingValues) ) { - - TagViewContainerLayout( tagViewContainerModifiers = tagViewContainerModifiers, content = { @@ -107,6 +116,8 @@ fun TagViewContainerLayout( tagViewContainerModifiers: TagViewContainerModifiers, content: @Composable () -> Unit ) { + val localDensity = LocalDensity.current + Layout(content = content) { measurables, constraints -> val looseConstraints = constraints.copy( minWidth = 0, @@ -138,8 +149,6 @@ fun TagViewContainerLayout( .toInt() ) } - Log.i("check_measure", "common: ${currentOffset}") - placeAble to currentOffset.also { currentOffset = it.copy( x = it.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() @@ -148,34 +157,25 @@ fun TagViewContainerLayout( } } - layout(width = constraints.maxWidth, - height = placeAbles.lastOrNull()?.run { first.height } ?: 0) { + layout( + width = constraints.maxWidth, + height = constraints.maxHeight + ) { Log.i("check_placeAble", "size: ${placeAbles.size}") placeAbles.forEachIndexed { index, placeable -> val (placeable, offset) = placeable //check whether current item has enough space - val currentItemHorizontalSpace = - placeable.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() - .toInt() - val currentItemVerticalSpace = - placeable.height + tagViewContainerModifiers.tagSpacingVertical.toPx() - .toInt() - - if (offset.x + currentItemHorizontalSpace < constraints.maxWidth && offset.y + currentItemVerticalSpace < constraints.maxHeight) { + if (offset.x + placeable.width < constraints.maxWidth && offset.y + placeable.height < constraints.maxHeight) { //current item has space Log.i("check_placeable", "index: $index current item has space") val nextItemIndex = index + 1 if (nextItemIndex <= placeAbles.lastIndex) { val nextItemOffset = placeAbles[nextItemIndex].second - - val nextItemHorizontalSpace = - placeAbles[nextItemIndex].first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() - .toInt() - val nextItemVerticalSpace = - placeAbles[nextItemIndex].first.height + tagViewContainerModifiers.tagSpacingVertical.toPx() - .toInt() - - if (nextItemOffset.x + nextItemHorizontalSpace < constraints.maxWidth && nextItemOffset.y + nextItemVerticalSpace < constraints.maxHeight) { + Log.i( + "check_placeable", + "next index: $nextItemIndex nextItemOffset : $nextItemOffset Before checking" + ) + if (nextItemOffset.x + placeAbles[nextItemIndex].first.width < constraints.maxWidth && nextItemOffset.y + placeAbles[nextItemIndex].first.height < constraints.maxHeight) { //next item has space Log.i( "check_placeable", @@ -185,54 +185,99 @@ fun TagViewContainerLayout( } else { //next item has no space //check space to accommodate more tag - Log.i("check_placeable", "index: $index next item has no space") - val moreTagPlaceAble = placeAbles.last() - val remainingTags = placeAbles.lastIndex - 1 - index - - -// tagViewContainerModifiers.moreTagConfiguration.overFlowText.invoke(remainingTags) - val moreTagHorizontalSpace = - moreTagPlaceAble.first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() - .toInt() - val moreTagVerticalSpace = - moreTagPlaceAble.first.height + tagViewContainerModifiers.tagSpacingVertical.toPx() - .toInt() - - if (offset.x + moreTagHorizontalSpace < constraints.maxWidth && offset.y + moreTagVerticalSpace < constraints.maxHeight) { - //place more tag - Log.i("check_placeable", "index: $index more tag placed") - moreTagPlaceAble.first.place(offset.x, offset.y) - return@layout - } else { - Log.i("check_placeable", "index: $index more tag has no space") + val overflow = showOverFlow( + index, + placeAbles, + tagViewContainerModifiers, + constraints, + localDensity + ) + overflow?.let { + it.first.place(it.second) } + return@layout } } } else { //current item has no space //check space to accommodate more tag - Log.i("check_placeable", "index: $index current item has no space") - val remainingTags = placeAbles.lastIndex - 1 - index - val moreTagPlaceAble = placeAbles.last() - val moreTagHorizontalSpace = - moreTagPlaceAble.first.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() - .toInt() - val moreTagVerticalSpace = - moreTagPlaceAble.first.height + tagViewContainerModifiers.tagSpacingVertical.toPx() - .toInt() - - if (offset.x + moreTagHorizontalSpace < constraints.maxWidth && offset.y + moreTagVerticalSpace < constraints.maxHeight) { - //place more tag - Log.i("check_placeable", "index: $index more tag placed") - placeable.place(offset.x, offset.y) - return@layout - } else { - Log.i("check_placeable", "index: $index more tag has no space") + val overflow = showOverFlow( + index, + placeAbles, + tagViewContainerModifiers, + constraints, + localDensity + ) + overflow?.let { + it.first.place(it.second) } + return@layout + } + } + } + } +} + +/** + * Used for displaying the over flow details when tags not fit in the container + * @param index current placeAble index + * @param placeAbles placeAble details of tags + * @param tagViewContainerModifiers collection of modifier elements that decorate or add behavior to tag view container + * @param constraints immutable constraints for measuring layouts + * @param localDensity A density of the screen. Used for the conversions between pixels and Dp + */ +fun showOverFlow( + index: Int, + placeAbles: List>, + tagViewContainerModifiers: TagViewContainerModifiers, + constraints: Constraints, + localDensity: Density +): Pair? { + val offset = placeAbles[index].second + val placeable = placeAbles[index] + + if (tagViewContainerModifiers.moreTagConfiguration.showOverFlow) { + Log.i("check_placeable", "index: $index next item has no space") + val moreTagPlaceAble = placeAbles.last() + val remainingTags = placeAbles.lastIndex - 1 - index + + if (offset.x + moreTagPlaceAble.first.width < constraints.maxWidth && offset.y + moreTagPlaceAble.first.height < constraints.maxHeight) { + //place more tag + Log.i( + "check_placeable", + "index: $index more tag placed offset: $offset" + ) + + //check whether more tag can be placed in between current and previous offset + val previousIndex = index - 1 + if (previousIndex >= 0) { + val previousOffset = placeAbles[previousIndex].second + val previousTag = placeAbles[previousIndex].first + + val moreTagXOffset = + previousOffset.x + localDensity.run { tagViewContainerModifiers.tagSpacingHorizontal.toPx() } + .toInt() + previousTag.width + val moreTagYOffset = previousOffset.y + + if (moreTagXOffset + moreTagPlaceAble.first.width < constraints.maxWidth && moreTagYOffset + moreTagPlaceAble.first.height < constraints.maxHeight) { + Log.i( + "check_placeable", + " prev index: $previousIndex more tag placed on in btw offset: $previousOffset" + ) + + return Pair(moreTagPlaceAble.first, IntOffset(moreTagXOffset, moreTagYOffset)) } } + + return moreTagPlaceAble + } else { + Log.i("check_placeable", "index: $index more tag has no space") } + } else { + return placeable } + + return null } @Preview(name = "Default Tag container") 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 b668bd2..a6062d7 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 @@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp data class TagViewContainerModifiers( val minWidth: Dp, val minHeight: Dp, - val width: Dp?, + val width: Dp, val height: Dp, val enableBorder: Boolean, val borderWidth: Dp, @@ -45,8 +45,8 @@ data class TagViewContainerModifiers( class Builder { private var minWidth: Dp = 150.dp private var minHeight: Dp = 150.dp - private var width: Dp? = null - private var height: Dp = minHeight + private var width: 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 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 0b719e6..243fc47 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 @@ -17,5 +17,6 @@ data class TagViewData( val leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, val trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, val enabled: Boolean = true, + val showOverFlow: Boolean = true, val overFlowText: (Int) -> String = { _ -> "" } ) 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 5209bcd..e50cea4 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 @@ -32,12 +32,12 @@ 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.model.TagViewModifiers -import co.yml.coreui.ui.R 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 @ExperimentalMaterial3Api @@ -292,14 +292,14 @@ fun ShadowTag() { } @Composable -fun DefaultTagViewContainer(){ +fun DefaultTagViewContainer() { val tagViewModifiers = TagViewModifiers.Builder() .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) .style(textStyle) .onCLick { - Log.i("check_click","tag view clicked") + Log.i("check_click", "tag view clicked") //remove the current tag view from the list } .build() @@ -308,34 +308,55 @@ fun DefaultTagViewContainer(){ TagViewData("Tag view 1", tagViewModifiers), TagViewData("Tag view 2", tagViewModifiers), TagViewData("Tag view 3", tagViewModifiers), -// TagViewData("Tag view 4", tagViewModifiers), -// TagViewData("Tag view 5", tagViewModifiers), -// TagViewData("Tag view 6", tagViewModifiers), -// TagViewData("Tag view 7", tagViewModifiers) + TagViewData("Tag view 4", tagViewModifiers), + TagViewData("Tag view 5", tagViewModifiers), + TagViewData("Tag view 6", tagViewModifiers), + TagViewData("Tag view 7", tagViewModifiers), + TagViewData("Tag view 8", tagViewModifiers), + TagViewData("Tag view 9", tagViewModifiers), + TagViewData("Tag view 10", tagViewModifiers), + TagViewData("Tag view 11", tagViewModifiers), + TagViewData("Tag view 12", tagViewModifiers), + TagViewData("Tag view 13", tagViewModifiers) ) val tagViewContainerModifiers = TagViewContainerModifiers.Builder() - .width(220.dp) - .height(100.dp) .containerPaddingValues(PaddingValues(8.dp)) .enableBorder(true) .shape(RoundedCornerShape(4.dp)) - .tagSpacingVertical(16.dp) + .tagSpacingVertical(8.dp) .tagSpacingHorizontal(8.dp) - .moreTagConfiguration(TagViewData(text = "more ...", overFlowText = {count -> - "$count more items" - }, tagViewModifiers = TagViewModifiers.Builder().onCLick { Log.i("check_click","more tag clicked") }.build())) - .onCLick {item -> + .backgroundColor(Color.Gray) + .width(250.dp) + .height(250.dp) + .moreTagConfiguration( + TagViewData( + text = "more", + overFlowText = { count -> + "$count more items" + }, + tagViewModifiers = TagViewModifiers.Builder().backgroundColor(Color.Gray) + .textColor(Color.White) + .onCLick { Log.i("check_click", "more tag clicked") }.build() + ) + ) + .onCLick { item -> val itemIndex = tagViewData.indexOf(item) - val updatedList = tagViewData.toMutableList() + val updatedList = tagViewData.toMutableList() - if (itemIndex!= -1){ + if (itemIndex != -1) { updatedList.removeAt(itemIndex) } - Log.i("check_click","tag view container item $itemIndex nam: ${item.text} clicked currentList size: ${tagViewData.size} updatedList size: ${updatedList.size}") + Log.i( + "check_click", + "tag view container item $itemIndex name: ${item.text} clicked currentList size: ${tagViewData.size} updatedList size: ${updatedList.size}" + ) } .build() - TagViewContainer(tagViewData = tagViewData, tagViewContainerModifiers = tagViewContainerModifiers) + TagViewContainer( + tagViewData = tagViewData, + tagViewContainerModifiers = tagViewContainerModifiers + ) } From aa1ac76ad5f7b762abb8142e5a3d5f9f09f45de8 Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Mon, 10 Apr 2023 18:10:52 +0530 Subject: [PATCH 4/8] 1. Added semantics property in TagViewModifiers.kt and TagViewContainerModifiers.kt. 2. Code refactoring --- .../coreui/ui/ytag/TagViewContainerTesting.kt | 4 +- .../co/yml/coreui/core/ui/ytag/TagView.kt | 2 +- .../coreui/core/ui/ytag/TagViewContainer.kt | 59 +++++-------------- .../ytag/model/TagViewContainerModifiers.kt | 9 ++- .../core/ui/ytag/model/TagViewModifiers.kt | 9 ++- .../coreui/feature/ytag/ui/YTagActivity.kt | 13 +++- 6 files changed, 43 insertions(+), 53 deletions(-) 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 7cd908c..96fcf88 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 @@ -85,7 +85,7 @@ class TagViewContainerTesting { fun tagViewContainer_tags_shown(){ launchTagViewContainer() - composeTestRule.onNodeWithContentDescription("Tag 1").assertIsDisplayed() + composeTestRule.onNodeWithText("Tag 1").assertIsDisplayed() } @Test @@ -97,6 +97,6 @@ class TagViewContainerTesting { launchTagViewContainer(tagViewContainerModifiers) - composeTestRule.onNodeWithContentDescription("more").assertIsDisplayed() + composeTestRule.onNodeWithText("more").assertIsDisplayed() } } 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 1a0c428..2564ff3 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 @@ -96,7 +96,7 @@ fun TagView( ) .align(Alignment.CenterVertically) .semantics { - this.contentDescription = text + this.contentDescription = semantics }, style = style, textDecoration = textDecoration, 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 ccd6425..1cd2763 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,6 +1,5 @@ package co.yml.coreui.core.ui.ytag -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -34,7 +33,7 @@ fun TagViewContainer( tagViewData: List, tagViewContainerModifiers: TagViewContainerModifiers ) { - //add more tag into the list + //add overflow details tag into the list val updatedTagViewData = tagViewData.toMutableList() val moreTag = tagViewContainerModifiers.moreTagConfiguration updatedTagViewData.add(moreTag) @@ -69,7 +68,7 @@ fun TagViewContainer( .clickable { } .semantics { this.contentDescription = - context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) + tagViewContainerModifiers.semantics.ifEmpty { context.getString(co.yml.coreui.ui.R.string.tag_view_container_accessibility_title) } } .testTag("tag_view_container") .background( @@ -127,20 +126,14 @@ fun TagViewContainerLayout( var currentRow = 0 var currentOffset = IntOffset.Zero + //Measurement phase val placeAbles = measurables.map { measurable -> val placeAble: Placeable = measurable.measure(looseConstraints) + //calculate the offsets to place the tags in layout phase if (currentOffset.x > 0f && currentOffset.x + placeAble.width + tagViewContainerModifiers.tagSpacingHorizontal.toPx() .toInt() > constraints.maxWidth ) { - Log.i( - "check_measure", - "c.XOffset: ${currentOffset.x} p.Width: ${placeAble.width} p.Height: ${placeAble.height} tagHSpace: ${ - tagViewContainerModifiers.tagSpacingHorizontal.toPx().toInt() - } tagVSpace: ${ - tagViewContainerModifiers.tagSpacingVertical.toPx().toInt() - } max w: ${constraints.maxWidth}" - ) currentRow += 1 currentOffset = currentOffset.copy( @@ -161,30 +154,21 @@ fun TagViewContainerLayout( width = constraints.maxWidth, height = constraints.maxHeight ) { - Log.i("check_placeAble", "size: ${placeAbles.size}") - placeAbles.forEachIndexed { index, placeable -> - val (placeable, offset) = placeable - //check whether current item has enough space + placeAbles.forEachIndexed { index, tagPlaceable -> + 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) { - //current item has space - Log.i("check_placeable", "index: $index current item has space") + //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 - Log.i( - "check_placeable", - "next index: $nextItemIndex nextItemOffset : $nextItemOffset Before checking" - ) if (nextItemOffset.x + placeAbles[nextItemIndex].first.width < constraints.maxWidth && nextItemOffset.y + placeAbles[nextItemIndex].first.height < constraints.maxHeight) { - //next item has space - Log.i( - "check_placeable", - "next index: $nextItemIndex nextItemOffset : $nextItemOffset next item has space and placed" - ) + //space available for next tag placeable.place(offset.x, offset.y) } else { - //next item has no space - //check space to accommodate more tag + //space not available for next tag + //place the over flow tag val overflow = showOverFlow( index, placeAbles, @@ -199,8 +183,8 @@ fun TagViewContainerLayout( } } } else { - //current item has no space - //check space to accommodate more tag + //space available for current tag + //place the over flow tag val overflow = showOverFlow( index, placeAbles, @@ -237,18 +221,12 @@ fun showOverFlow( val placeable = placeAbles[index] if (tagViewContainerModifiers.moreTagConfiguration.showOverFlow) { - Log.i("check_placeable", "index: $index next item has no space") val moreTagPlaceAble = placeAbles.last() val remainingTags = placeAbles.lastIndex - 1 - index if (offset.x + moreTagPlaceAble.first.width < constraints.maxWidth && offset.y + moreTagPlaceAble.first.height < constraints.maxHeight) { //place more tag - Log.i( - "check_placeable", - "index: $index more tag placed offset: $offset" - ) - - //check whether more tag can be placed in between current and previous offset + //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 @@ -260,18 +238,11 @@ fun showOverFlow( val moreTagYOffset = previousOffset.y if (moreTagXOffset + moreTagPlaceAble.first.width < constraints.maxWidth && moreTagYOffset + moreTagPlaceAble.first.height < constraints.maxHeight) { - Log.i( - "check_placeable", - " prev index: $previousIndex more tag placed on in btw offset: $previousOffset" - ) - return Pair(moreTagPlaceAble.first, IntOffset(moreTagXOffset, moreTagYOffset)) } } return moreTagPlaceAble - } else { - Log.i("check_placeable", "index: $index more tag has no space") } } else { return placeable 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 a6062d7..bf57331 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 @@ -39,7 +39,8 @@ data class TagViewContainerModifiers( val tagSpacingHorizontal: Dp, val tagSpacingVertical: Dp, val moreTagConfiguration: TagViewData, - val onClick: (TagViewData) -> Unit + val onClick: (TagViewData) -> Unit, + val semantics: String ) { //todo sree_ check min and max default size class Builder { @@ -65,6 +66,7 @@ data class TagViewContainerModifiers( .build() ) private var onClick: (TagViewData) -> Unit = {} + private var semantics: String = "" fun minWidth(minWidth: Dp) = apply { this.minWidth = minWidth } @@ -97,6 +99,8 @@ data class TagViewContainerModifiers( fun onCLick(onClick: (TagViewData) -> Unit) = apply { this.onClick = onClick } + fun semantics(semantics: String) = apply { this.semantics = semantics } + fun build() = TagViewContainerModifiers( minWidth, minHeight, @@ -111,7 +115,8 @@ data class TagViewContainerModifiers( tagSpacingHorizontal, tagSpacingVertical, moreTagConfiguration, - onClick + onClick, + semantics ) } } 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 4f9b96c..a33dd5a 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 @@ -79,7 +79,8 @@ data class TagViewModifiers( val tonalElevation: Dp, val shadowElevation: Dp, val containerPaddingValues: PaddingValues, - val onClick: () -> Unit + val onClick: () -> Unit, + val semantics: String ) { class Builder { private var minWidth: Dp = 52.dp @@ -111,6 +112,7 @@ data class TagViewModifiers( private var shadowElevation: Dp = 0.dp private var containerPaddingValues: PaddingValues = PaddingValues(horizontal = 4.dp) private var onClick: () -> Unit = {} + private var semantics: String = text fun minWidth(minWidth: Dp) = apply { this.minWidth = minWidth } @@ -166,6 +168,8 @@ data class TagViewModifiers( apply { this.containerPaddingValues = paddingValues } fun onCLick(onClick: () -> Unit) = apply { this.onClick = onClick } + + fun semantics(semantics: String) = apply { this.semantics = semantics } fun build() = TagViewModifiers( minWidth, minHeight, @@ -194,7 +198,8 @@ data class TagViewModifiers( tonalElevation, shadowElevation, containerPaddingValues, - onClick + onClick, + semantics ) } } 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 e50cea4..e5e0228 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 @@ -306,8 +306,17 @@ fun DefaultTagViewContainer() { val tagViewData = listOf( TagViewData("Tag view 1", tagViewModifiers), - TagViewData("Tag view 2", tagViewModifiers), - TagViewData("Tag view 3", tagViewModifiers), + TagViewData("Tag 2", TagViewModifiers.Builder() + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .style(textStyle) + .semantics("second tag") + .onCLick { + Log.i("check_click", "tag view clicked") + } + .build()), + TagViewData("Tag 3", tagViewModifiers), TagViewData("Tag view 4", tagViewModifiers), TagViewData("Tag view 5", tagViewModifiers), TagViewData("Tag view 6", tagViewModifiers), From 14acc31d03fb592b663f7b21b9eca1a8782cc68e Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Mon, 10 Apr 2023 20:42:57 +0530 Subject: [PATCH 5/8] Replaced Row layout with constraint layout in TagView to make text view fit in the tag view. --- .../co/yml/coreui/core/ui/ytag/TagView.kt | 45 +++++++++++++++---- .../coreui/core/ui/ytag/TagViewContainer.kt | 6 ++- .../ytag/model/TagViewContainerModifiers.kt | 2 +- .../core/ui/ytag/model/TagViewModifiers.kt | 1 + .../coreui/feature/ytag/ui/YTagActivity.kt | 22 +++++++++ gradle/libs.versions.toml | 4 +- 6 files changed, 67 insertions(+), 13 deletions(-) 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 2564ff3..9f72f73 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 @@ -7,9 +7,11 @@ 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.material3.* +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.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape @@ -21,7 +23,8 @@ 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 co.yml.coreui.core.ui.ytag.model.TagViewData +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension import co.yml.coreui.core.ui.ytag.model.TagViewModifiers /** @@ -53,8 +56,10 @@ fun TagView( .width(width = width ?: Dp.Unspecified) .height(height = height) ) { - Row( + ConstraintLayout( modifier = Modifier + .width(width = width ?: Dp.Unspecified) + .height(height) .run { if (enableBorder) { border( @@ -77,10 +82,18 @@ fun TagView( .background( color = backgroundColor, shape = shape - ), - verticalAlignment = Alignment.CenterVertically + ) ) { - leadingIcon?.invoke(enabled) + val (leading_icon, text_view, trailing_icon) = createRefs() + + Box(modifier = Modifier.constrainAs(leading_icon) { + start.linkTo(parent.start) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + } + ) { + leadingIcon?.invoke(enabled) + } Text( text = overFlowText.ifEmpty { text }, @@ -91,10 +104,16 @@ fun TagView( 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 ) - .align(Alignment.CenterVertically) .semantics { this.contentDescription = semantics }, @@ -107,7 +126,15 @@ fun TagView( maxLines = maxLines, onTextLayout = onTextLayout ) - trailingIcon?.invoke(enabled) + Box(modifier = Modifier.constrainAs(trailing_icon) { + end.linkTo(parent.end) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + } + ) { + trailingIcon?.invoke(enabled) + } + } } } 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 1cd2763..36872d5 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 @@ -222,7 +222,6 @@ fun showOverFlow( if (tagViewContainerModifiers.moreTagConfiguration.showOverFlow) { val moreTagPlaceAble = placeAbles.last() - val remainingTags = placeAbles.lastIndex - 1 - index if (offset.x + moreTagPlaceAble.first.width < constraints.maxWidth && offset.y + moreTagPlaceAble.first.height < constraints.maxHeight) { //place more tag @@ -238,10 +237,13 @@ fun showOverFlow( val moreTagYOffset = previousOffset.y if (moreTagXOffset + moreTagPlaceAble.first.width < constraints.maxWidth && moreTagYOffset + moreTagPlaceAble.first.height < constraints.maxHeight) { + val remainingTags = placeAbles.lastIndex - index + tagViewContainerModifiers.moreTagConfiguration.overFlowText.invoke(remainingTags) return Pair(moreTagPlaceAble.first, IntOffset(moreTagXOffset, moreTagYOffset)) } } - + val remainingTags = placeAbles.lastIndex - index + tagViewContainerModifiers.moreTagConfiguration.overFlowText.invoke(remainingTags) return moreTagPlaceAble } } else { 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 bf57331..778ffb8 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 @@ -23,7 +23,7 @@ import androidx.compose.ui.unit.dp * @param containerPaddingValues define padding for TagViewContainer * @param tagSpacingHorizontal horizontal padding between tag views * @param tagSpacingVertical vertical padding between tag views - * + * @param semantics add content description for tag view container */ data class TagViewContainerModifiers( val minWidth: Dp, 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 a33dd5a..63a1869 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 @@ -50,6 +50,7 @@ import androidx.compose.ui.unit.sp * @param shadowElevation The size of the shadow below the surface. * @param containerPaddingValues define padding for TagView * @param onClick perform click event + * @param semantics add content description for tag view */ data class TagViewModifiers( val minWidth: Dp, 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 e5e0228..33ea924 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 @@ -27,6 +27,7 @@ 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.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -104,6 +105,7 @@ fun DefaultTag() { @Composable fun CapsuleTag() { val tagViewModifiers = TagViewModifiers.Builder() + .width(100.dp) .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -119,6 +121,7 @@ fun CapsuleTag() { @Composable fun RectangleTag() { val tagViewModifiers = TagViewModifiers.Builder() + .width(100.dp) .shape(RectangleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -135,6 +138,7 @@ fun RectangleTag() { fun RoundRectangleTag() { val tagViewModifiers = TagViewModifiers.Builder() .shape(RoundedCornerShape(dimensionResource(id = R.dimen.padding_small))) + .width(100.dp) .backgroundColor(Color.Black) .textColor(Color.White) .style(textStyle) @@ -150,6 +154,9 @@ fun RoundRectangleTag() { fun TagWithLeadingIcon() { val context = LocalContext.current val tagViewModifiers = TagViewModifiers.Builder() + .width(100.dp) + .maxLines(1) + .overFlow(TextOverflow.Ellipsis) .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -178,6 +185,10 @@ fun TagWithLeadingIcon() { fun TagWithTrailingIcon() { val context = LocalContext.current val tagViewModifiers = TagViewModifiers.Builder() + .width(100.dp) + .maxLines(1) + .textAlign(TextAlign.Start) + .overFlow(TextOverflow.Ellipsis) .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -206,6 +217,9 @@ fun TagWithTrailingIcon() { fun TagWithLeadingTrailingIcon() { val context = LocalContext.current val tagViewModifiers = TagViewModifiers.Builder() + .width(100.dp) + .maxLines(1) + .overFlow(TextOverflow.Ellipsis) .shape(CircleShape) .backgroundColor(Color.Black) .maxLines(1) @@ -258,6 +272,7 @@ fun TagWithLeadingTrailingIcon() { @Composable fun BorderTag() { val tagViewModifiers = TagViewModifiers.Builder() + .width(100.dp) .textColor(Color.Black) .enableBorder(true) .borderColor(Color.Red) @@ -276,6 +291,7 @@ fun BorderTag() { @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) @@ -295,9 +311,12 @@ fun ShadowTag() { fun DefaultTagViewContainer() { val tagViewModifiers = TagViewModifiers.Builder() .shape(CircleShape) + .width(100.dp) .backgroundColor(Color.Black) .textColor(Color.White) .style(textStyle) + .maxLines(1) + .overFlow(TextOverflow.Ellipsis) .onCLick { Log.i("check_click", "tag view clicked") //remove the current tag view from the list @@ -307,6 +326,7 @@ fun DefaultTagViewContainer() { val tagViewData = listOf( TagViewData("Tag view 1", tagViewModifiers), TagViewData("Tag 2", TagViewModifiers.Builder() + .width(100.dp) .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -342,9 +362,11 @@ fun DefaultTagViewContainer() { TagViewData( text = "more", overFlowText = { count -> + Log.i("check_over_flow","remaining tags: $count") "$count more items" }, tagViewModifiers = TagViewModifiers.Builder().backgroundColor(Color.Gray) + .width(100.dp) .textColor(Color.White) .onCLick { Log.i("check_click", "more tag clicked") }.build() ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e673153..505c3ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,6 +23,7 @@ androidxTestRunner = "1.5.1" androidxTestMonitor = "1.6.0" androidxTestCore = "1.4.0" androidxTestExt = "1.1.4" +constraintlayout = "1.0.1" #hilt hilt = "2.44" @@ -94,6 +95,7 @@ androidx-navigation-compose = { group = "androidx.navigation", name = "navigatio androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" } androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constraintlayout" } #hilt hilt_compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } @@ -130,7 +132,7 @@ test_runner = { module = "androidx.test:runner", version.ref = "test_runner" } [bundles] # Define bundles/groups of libraries -compose = ["androidx.activity.compose", "androidx.compose.foundation", "androidx.navigation.compose", "androidx.compose.material3", "androidx.compose.material3.windowSizeClass", "androidx.compose.runtime","androidx.compose.ui.tooling","androidx.compose.ui.tooling.preview"] +compose = ["androidx.activity.compose", "androidx.compose.foundation", "androidx.navigation.compose", "androidx.compose.material3", "androidx.compose.material3.windowSizeClass", "androidx.compose.runtime","androidx.compose.ui.tooling","androidx.compose.ui.tooling.preview", "androidx.constraintlayout"] test = ["androidx.test.core","androidx.test.ext","androidx.test.rules","androidx.test.runner", "compose_ui_testing"] coroutine_test = ["coroutine.test", "coroutine.turbine"] hilt = ["hilt.compiler","hilt.android","hilt.test"] From 2a16eef87c1aeb705666fc9d234d0a30520c9dd8 Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Mon, 10 Apr 2023 21:37:16 +0530 Subject: [PATCH 6/8] Added remaining item text change for over flow tag. --- .../coreui/core/ui/ytag/TagViewContainer.kt | 48 +++++++++++++++---- .../coreui/core/ui/ytag/model/TagViewData.kt | 6 ++- .../coreui/feature/ytag/ui/YTagActivity.kt | 8 ++-- 3 files changed, 45 insertions(+), 17 deletions(-) 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 36872d5..76f69ef 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 @@ -6,7 +6,7 @@ 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.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout @@ -34,9 +34,14 @@ fun TagViewContainer( tagViewContainerModifiers: TagViewContainerModifiers ) { //add overflow details tag into the list - val updatedTagViewData = tagViewData.toMutableList() + val overFlowText = remember { + mutableStateOf("") + } + val moreTag = tagViewContainerModifiers.moreTagConfiguration - updatedTagViewData.add(moreTag) + val remainingTags: (Int) -> Unit = { count -> + overFlowText.value = moreTag.overFlowText.invoke(count) + } with(tagViewContainerModifiers) { val context = LocalContext.current @@ -82,9 +87,10 @@ fun TagViewContainer( modifier = modifier ) { TagViewContainerLayout( + remainingTags = remainingTags, tagViewContainerModifiers = tagViewContainerModifiers, content = { - updatedTagViewData.forEach { + tagViewData.forEach { with(it) { val containerItemClick = { tagViewContainerModifiers.onClick.invoke(it) @@ -100,6 +106,22 @@ fun TagViewContainer( ) } } + + //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 + ) + } }) } } @@ -108,10 +130,12 @@ fun TagViewContainer( /** * [TagViewContainerLayout] used for creating a custom layout to hosting y tag * @param tagViewContainerModifiers collection of modifier elements that decorate or add behavior to tag view container - * @param content content of the container [Tag views] + * @param content content of the tag view container + * @param remainingTags return item count which are not rendered in the tag view container */ @Composable fun TagViewContainerLayout( + remainingTags: (Int) -> Unit, tagViewContainerModifiers: TagViewContainerModifiers, content: @Composable () -> Unit ) { @@ -174,7 +198,8 @@ fun TagViewContainerLayout( placeAbles, tagViewContainerModifiers, constraints, - localDensity + localDensity, + remainingTags ) overflow?.let { it.first.place(it.second) @@ -190,7 +215,8 @@ fun TagViewContainerLayout( placeAbles, tagViewContainerModifiers, constraints, - localDensity + localDensity, + remainingTags ) overflow?.let { it.first.place(it.second) @@ -209,13 +235,15 @@ fun TagViewContainerLayout( * @param tagViewContainerModifiers collection of modifier elements that decorate or add behavior to tag view container * @param constraints immutable constraints for measuring layouts * @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( index: Int, placeAbles: List>, tagViewContainerModifiers: TagViewContainerModifiers, constraints: Constraints, - localDensity: Density + localDensity: Density, + remainingItems: (Int) -> Unit ): Pair? { val offset = placeAbles[index].second val placeable = placeAbles[index] @@ -238,12 +266,12 @@ fun showOverFlow( if (moreTagXOffset + moreTagPlaceAble.first.width < constraints.maxWidth && moreTagYOffset + moreTagPlaceAble.first.height < constraints.maxHeight) { val remainingTags = placeAbles.lastIndex - index - tagViewContainerModifiers.moreTagConfiguration.overFlowText.invoke(remainingTags) + remainingItems.invoke(remainingTags) return Pair(moreTagPlaceAble.first, IntOffset(moreTagXOffset, moreTagYOffset)) } } val remainingTags = placeAbles.lastIndex - index - tagViewContainerModifiers.moreTagConfiguration.overFlowText.invoke(remainingTags) + remainingItems.invoke(remainingTags) return moreTagPlaceAble } } else { 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 243fc47..e1953ae 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 @@ -5,14 +5,16 @@ import androidx.compose.runtime.Composable /** * [TagViewData] Used for holding the TagView data * - * @param text text the text to be displayed + * @param text Tag view text to be displayed * @param leadingIcon the optional leading icon to be displayed at the beginning of the TagView * @param trailingIcon the optional leading icon to be displayed at the end of the TagView * @param enabled controls the enabled state of the TagView * @param tagViewModifiers collection of modifier elements that decorate or add behavior to TagView elements + * @param showOverFlow show or hide over flow text + * @param overFlowText to be displayed for over flow tag [use overFlowText instead of [text] for over flow tag ] */ data class TagViewData( - val text: String, + val text: String = "", val tagViewModifiers: TagViewModifiers = TagViewModifiers.Builder().build(), val leadingIcon: @Composable ((enable: Boolean) -> Unit)? = null, val trailingIcon: @Composable ((enable: Boolean) -> Unit)? = null, 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 33ea924..c263fc5 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 @@ -357,13 +357,11 @@ fun DefaultTagViewContainer() { .tagSpacingHorizontal(8.dp) .backgroundColor(Color.Gray) .width(250.dp) - .height(250.dp) + .height(240.dp) .moreTagConfiguration( TagViewData( - text = "more", - overFlowText = { count -> - Log.i("check_over_flow","remaining tags: $count") - "$count more items" + overFlowText = {count -> + "$count more" }, tagViewModifiers = TagViewModifiers.Builder().backgroundColor(Color.Gray) .width(100.dp) From 986cfc3521a24393223a94ab255ab9e9b3169599 Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Tue, 11 Apr 2023 10:33:47 +0530 Subject: [PATCH 7/8] Updated catalog app home screen elements properties. --- .../core/ui/ytag/model/TagViewModifiers.kt | 2 +- core/ui/src/main/res/values/colors.xml | 4 ++ .../coreui/feature/ytag/ui/YTagActivity.kt | 67 ++++++++++++++----- 3 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 core/ui/src/main/res/values/colors.xml 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 63a1869..fa693ba 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 @@ -84,7 +84,7 @@ data class TagViewModifiers( val semantics: String ) { class Builder { - private var minWidth: Dp = 52.dp + private var minWidth: Dp = 80.dp private var minHeight: Dp = 32.dp private var width: Dp? = null private var height: Dp = minHeight diff --git a/core/ui/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml new file mode 100644 index 0000000..88a9380 --- /dev/null +++ b/core/ui/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #E0E0E0 + 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 c263fc5..54b891d 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 @@ -70,8 +70,8 @@ class YTagActivity : ComponentActivity() { items(10) { item -> when (item) { 0 -> CapsuleTag() -// 1 -> RectangleTag() -// 2 -> RoundRectangleTag() + 1 -> RectangleTag() + 2 -> RoundRectangleTag() 3 -> DefaultTag() 4 -> TagWithLeadingIcon() 5 -> TagWithTrailingIcon() @@ -105,7 +105,6 @@ fun DefaultTag() { @Composable fun CapsuleTag() { val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -121,7 +120,7 @@ fun CapsuleTag() { @Composable fun RectangleTag() { val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) + .width(90.dp) .shape(RectangleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -138,7 +137,7 @@ fun RectangleTag() { fun RoundRectangleTag() { val tagViewModifiers = TagViewModifiers.Builder() .shape(RoundedCornerShape(dimensionResource(id = R.dimen.padding_small))) - .width(100.dp) + .width(120.dp) .backgroundColor(Color.Black) .textColor(Color.White) .style(textStyle) @@ -154,7 +153,7 @@ fun RoundRectangleTag() { fun TagWithLeadingIcon() { val context = LocalContext.current val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) + .width(120.dp) .maxLines(1) .overFlow(TextOverflow.Ellipsis) .shape(CircleShape) @@ -185,7 +184,7 @@ fun TagWithLeadingIcon() { fun TagWithTrailingIcon() { val context = LocalContext.current val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) + .width(150.dp) .maxLines(1) .textAlign(TextAlign.Start) .overFlow(TextOverflow.Ellipsis) @@ -217,7 +216,7 @@ fun TagWithTrailingIcon() { fun TagWithLeadingTrailingIcon() { val context = LocalContext.current val tagViewModifiers = TagViewModifiers.Builder() - .width(100.dp) + .width(140.dp) .maxLines(1) .overFlow(TextOverflow.Ellipsis) .shape(CircleShape) @@ -309,6 +308,9 @@ fun ShadowTag() { @Composable fun DefaultTagViewContainer() { + val context = LocalContext.current + val text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_leading_icon) + val tagViewModifiers = TagViewModifiers.Builder() .shape(CircleShape) .width(100.dp) @@ -326,7 +328,7 @@ fun DefaultTagViewContainer() { val tagViewData = listOf( TagViewData("Tag view 1", tagViewModifiers), TagViewData("Tag 2", TagViewModifiers.Builder() - .width(100.dp) + .width(60.dp) .shape(CircleShape) .backgroundColor(Color.Black) .textColor(Color.White) @@ -336,10 +338,42 @@ fun DefaultTagViewContainer() { Log.i("check_click", "tag view clicked") } .build()), - TagViewData("Tag 3", tagViewModifiers), + TagViewData("Tag 3", 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()), TagViewData("Tag view 4", tagViewModifiers), - TagViewData("Tag view 5", tagViewModifiers), - TagViewData("Tag view 6", tagViewModifiers), + TagViewData("Tag 5", tagViewModifiers.copy(width = 60.dp)), + TagViewData(text = "Tag view 6", tagViewModifiers = TagViewModifiers.Builder() + .width(120.dp) + .maxLines(1) + .textAlign(TextAlign.Start) + .overFlow(TextOverflow.Ellipsis) + .shape(CircleShape) + .backgroundColor(Color.Black) + .textColor(Color.White) + .build() + ,leadingIcon = { enabled -> + IconButton( + modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), + onClick = { + if (enabled) { + Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + } + }) { + Icon( + painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), + contentDescription = null, + tint = Color.White + ) + } + }), TagViewData("Tag view 7", tagViewModifiers), TagViewData("Tag view 8", tagViewModifiers), TagViewData("Tag view 9", tagViewModifiers), @@ -355,17 +389,18 @@ fun DefaultTagViewContainer() { .shape(RoundedCornerShape(4.dp)) .tagSpacingVertical(8.dp) .tagSpacingHorizontal(8.dp) - .backgroundColor(Color.Gray) + .backgroundColor(colorResource(id = R.color.light_gray)) .width(250.dp) - .height(240.dp) + .height(230.dp) .moreTagConfiguration( TagViewData( overFlowText = {count -> "$count more" }, - tagViewModifiers = TagViewModifiers.Builder().backgroundColor(Color.Gray) + tagViewModifiers = TagViewModifiers.Builder().backgroundColor(colorResource(id = R.color.light_gray)) .width(100.dp) - .textColor(Color.White) + .textColor(Color.Black) + .textAlign(TextAlign.Start) .onCLick { Log.i("check_click", "more tag clicked") }.build() ) ) From 50b893559b54f09829275f237234fb237e289f79 Mon Sep 17 00:00:00 2001 From: Sreekuttan C J Date: Tue, 11 Apr 2023 21:00:08 +0530 Subject: [PATCH 8/8] 1. Fixed more tag bugs 2. Code refactoring --- .../coreui/core/ui/ytag/TagViewContainer.kt | 104 ++-- core/ui/src/main/res/values/colors.xml | 9 + .../coreui/feature/ytag/ui/YTagActivity.kt | 502 ++++++++++-------- 3 files changed, 357 insertions(+), 258 deletions(-) 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 76f69ef..7cebc6e 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 @@ -179,49 +179,69 @@ fun TagViewContainerLayout( height = constraints.maxHeight ) { placeAbles.forEachIndexed { index, tagPlaceable -> - 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 - val overflow = showOverFlow( - index, - placeAbles, - tagViewContainerModifiers, - constraints, - localDensity, - remainingTags - ) - overflow?.let { - it.first.place(it.second) + 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 + } } - return@layout } + } 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) + } + return@layout } - } else { - //space 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) - } - return@layout } } } @@ -247,10 +267,8 @@ fun showOverFlow( ): Pair? { val offset = placeAbles[index].second val placeable = placeAbles[index] - 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 @@ -272,7 +290,7 @@ fun showOverFlow( } val remainingTags = placeAbles.lastIndex - index remainingItems.invoke(remainingTags) - return moreTagPlaceAble + return Pair(moreTagPlaceAble.first, IntOffset(offset.x, offset.y)) } } else { return placeable diff --git a/core/ui/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml index 88a9380..00a3be5 100644 --- a/core/ui/src/main/res/values/colors.xml +++ b/core/ui/src/main/res/values/colors.xml @@ -1,4 +1,13 @@ #E0E0E0 + #0C0A3E + #C8FFBE + #EDFFAB + #F9564F + #89608E + #E1F5FE + #01579B + #0288D1 + 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 54b891d..6bf076c 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 @@ -1,7 +1,7 @@ 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 @@ -60,10 +60,9 @@ class YTagActivity : ComponentActivity() { { Box( modifier = Modifier - .fillMaxWidth() + .fillMaxSize() .background(Color.White) - .padding(it), - contentAlignment = Alignment.TopCenter + .padding(it) ) { LazyColumn( content = { @@ -83,7 +82,9 @@ class YTagActivity : ComponentActivity() { } }, verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.padding_normal)), - modifier = Modifier.padding(dimensionResource(id = R.dimen.padding_normal_medium)) + modifier = Modifier + .padding(dimensionResource(id = R.dimen.padding_normal_medium)) + .align(Alignment.Center) ) } } @@ -104,166 +105,93 @@ fun DefaultTag() { @Composable fun CapsuleTag() { - val tagViewModifiers = TagViewModifiers.Builder() - .shape(CircleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .style(textStyle) - .build() + val context = LocalContext.current + val data = capsuleTagData( + context = context, + backgroundColor = colorResource(id = R.color.russian_violet) + ) TagView( - text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_capsule), - tagViewModifiers = tagViewModifiers + text = data.text, + tagViewModifiers = data.tagViewModifiers ) } @Composable fun RectangleTag() { - val tagViewModifiers = TagViewModifiers.Builder() - .width(90.dp) - .shape(RectangleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .style(textStyle) - .build() + val context = LocalContext.current + val data = + rectangleTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_green), + textColor = Color.Black + ) TagView( - text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_rectangle), - tagViewModifiers = tagViewModifiers + text = data.text, + tagViewModifiers = data.tagViewModifiers ) } @Composable fun RoundRectangleTag() { - val tagViewModifiers = TagViewModifiers.Builder() - .shape(RoundedCornerShape(dimensionResource(id = R.dimen.padding_small))) - .width(120.dp) - .backgroundColor(Color.Black) - .textColor(Color.White) - .style(textStyle) - .build() - + val context = LocalContext.current + val data = roundRectTagData( + context = context, + backgroundColor = colorResource(id = R.color.light_yellow), + textColor = Color.Black + ) TagView( - text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_round_rectangle), - tagViewModifiers = tagViewModifiers + text = data.text, + tagViewModifiers = data.tagViewModifiers ) } @Composable fun TagWithLeadingIcon() { val context = LocalContext.current - val tagViewModifiers = TagViewModifiers.Builder() - .width(120.dp) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .fontStyle(FontStyle.Italic) - .build() - val text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_leading_icon) - TagView(text = text, leadingIcon = { enabled -> - IconButton( - modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), - onClick = { - if (enabled) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() - } - }) { - Icon( - painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), - contentDescription = null, - tint = Color.White - ) - } - }, tagViewModifiers = tagViewModifiers) + 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 + ) } @Composable fun TagWithTrailingIcon() { val context = LocalContext.current - val tagViewModifiers = TagViewModifiers.Builder() - .width(150.dp) - .maxLines(1) - .textAlign(TextAlign.Start) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .fontSize(15.sp) - .build() - - val text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_trailing_icon) - TagView(text = text, trailingIcon = { enabled -> - IconButton(modifier = Modifier - .padding(end = dimensionResource(id = R.dimen.padding_medium)) - .size(dimensionResource(id = R.dimen.padding_normal_medium)), onClick = { - if (enabled) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() - } - }) { - Icon( - painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_close_20px), - contentDescription = null, - tint = Color.White - ) - } - }, tagViewModifiers = tagViewModifiers) + 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 tagViewModifiers = TagViewModifiers.Builder() - .width(140.dp) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(Color.Black) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .textColor(Color.White) - .onCLick { - - } - .build() + val data = leadingIconTrailingIconData( + context = context, + backgroundColor = Color.Black, + textColor = Color.White + ) TagView( - text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_leading_trailing_icon), - 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)), - onClick = { - if (enabled) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() - } - }) { - Icon( - painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), - contentDescription = null, - tint = Color.White - ) - } - }, - trailingIcon = { enabled -> - val text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_trailing_icon) - 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() - } - }) { - Icon( - painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_close_20px), - contentDescription = null, - tint = Color.White - ) - } - }, - tagViewModifiers = tagViewModifiers, + text = data.text, + leadingIcon = data.leadingIcon, + trailingIcon = data.trailingIcon, + tagViewModifiers = data.tagViewModifiers, enabled = false ) } @@ -309,78 +237,40 @@ fun ShadowTag() { @Composable fun DefaultTagViewContainer() { val context = LocalContext.current - val text = stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_leading_icon) - - val tagViewModifiers = TagViewModifiers.Builder() - .shape(CircleShape) - .width(100.dp) - .backgroundColor(Color.Black) - .textColor(Color.White) - .style(textStyle) - .maxLines(1) - .overFlow(TextOverflow.Ellipsis) - .onCLick { - Log.i("check_click", "tag view clicked") - //remove the current tag view from the list - } - .build() val tagViewData = listOf( - TagViewData("Tag view 1", tagViewModifiers), - TagViewData("Tag 2", TagViewModifiers.Builder() - .width(60.dp) - .shape(CircleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .style(textStyle) - .semantics("second tag") - .onCLick { - Log.i("check_click", "tag view clicked") - } - .build()), - TagViewData("Tag 3", 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()), - TagViewData("Tag view 4", tagViewModifiers), - TagViewData("Tag 5", tagViewModifiers.copy(width = 60.dp)), - TagViewData(text = "Tag view 6", tagViewModifiers = TagViewModifiers.Builder() - .width(120.dp) - .maxLines(1) - .textAlign(TextAlign.Start) - .overFlow(TextOverflow.Ellipsis) - .shape(CircleShape) - .backgroundColor(Color.Black) - .textColor(Color.White) - .build() - ,leadingIcon = { enabled -> - IconButton( - modifier = Modifier.size(dimensionResource(id = R.dimen.padding_normal_medium)), - onClick = { - if (enabled) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() - } - }) { - Icon( - painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), - contentDescription = null, - tint = Color.White - ) - } - }), - TagViewData("Tag view 7", tagViewModifiers), - TagViewData("Tag view 8", tagViewModifiers), - TagViewData("Tag view 9", tagViewModifiers), - TagViewData("Tag view 10", tagViewModifiers), - TagViewData("Tag view 11", tagViewModifiers), - TagViewData("Tag view 12", tagViewModifiers), - TagViewData("Tag view 13", tagViewModifiers) + + 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 tagViewContainerModifiers = TagViewContainerModifiers.Builder() @@ -389,19 +279,21 @@ fun DefaultTagViewContainer() { .shape(RoundedCornerShape(4.dp)) .tagSpacingVertical(8.dp) .tagSpacingHorizontal(8.dp) - .backgroundColor(colorResource(id = R.color.light_gray)) - .width(250.dp) - .height(230.dp) + .backgroundColor(colorResource(id = R.color.cyan_50)) + .width(260.dp) + .height(180.dp) .moreTagConfiguration( TagViewData( - overFlowText = {count -> - "$count more" + overFlowText = { count -> + "$count more" }, - tagViewModifiers = TagViewModifiers.Builder().backgroundColor(colorResource(id = R.color.light_gray)) - .width(100.dp) - .textColor(Color.Black) + tagViewModifiers = TagViewModifiers.Builder() + .backgroundColor(colorResource(id = R.color.cyan_50)) + .width(80.dp) .textAlign(TextAlign.Start) - .onCLick { Log.i("check_click", "more tag clicked") }.build() + .height(30.dp) + .textColor(Color.Black) + .onCLick { }.build() ) ) .onCLick { item -> @@ -411,11 +303,6 @@ fun DefaultTagViewContainer() { if (itemIndex != -1) { updatedList.removeAt(itemIndex) } - - Log.i( - "check_click", - "tag view container item $itemIndex name: ${item.text} clicked currentList size: ${tagViewData.size} updatedList size: ${updatedList.size}" - ) } .build() @@ -424,3 +311,188 @@ fun DefaultTagViewContainer() { tagViewContainerModifiers = tagViewContainerModifiers ) } + +/** + * @param context current context + * @param backgroundColor tag view background color + */ +fun capsuleTagData(context: Context, backgroundColor: 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() + ) +} + +/** + * @param context current context + * @param backgroundColor tag view background color + * @param textColor tag view text color + */ +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() + ) +} + +/** + * @param context current context + * @param backgroundColor tag view background color + * @param textColor tag view text color + */ +fun roundRectTagData(context: Context, backgroundColor: Color, textColor: Color): TagViewData { + return TagViewData( + 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) + .build() + ) +} + +/** + * @param context current context + * @param backgroundColor tag view background color + * @param textColor tag view text color + */ +fun leadingIconTagData(context: Context, backgroundColor: Color, textColor: Color): 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)), + onClick = { + if (enabled) { + Toast.makeText( + context, + context.getString(co.yml.coreui.feature.ytag.R.string.tag_leading_icon), + Toast.LENGTH_SHORT + ).show() + } + }) { + Icon( + painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), + contentDescription = null, + tint = Color.White + ) + } + }) +} + +/** + * @param context current context + * @param backgroundColor tag view background color + * @param textColor tag view text color + */ +fun trailingIconData(context: Context, backgroundColor: Color, textColor: Color): 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 -> + IconButton(modifier = Modifier + .padding(end = dimensionResource(id = R.dimen.padding_medium)) + .size(dimensionResource(id = R.dimen.padding_normal_medium)), onClick = { + if (enabled) { + Toast.makeText( + context, + context.getString(co.yml.coreui.feature.ytag.R.string.tag_trailing_icon), + Toast.LENGTH_SHORT + ).show() + } + }) { + Icon( + painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_close_20px), + contentDescription = null, + tint = Color.White + ) + } + }) +} + +/** + * @param context current context + * @param backgroundColor tag view background color + * @param textColor tag view text color + */ +fun leadingIconTrailingIconData( + context: Context, + backgroundColor: Color, + textColor: Color +): 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)), + onClick = { + if (enabled) { + Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + } + }) { + Icon( + painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_location_24px), + contentDescription = null, + tint = Color.White + ) + } + }, + trailingIcon = { enabled -> + val text = + stringResource(id = co.yml.coreui.feature.ytag.R.string.tag_trailing_icon) + 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() + } + }) { + Icon( + painter = painterResource(id = co.yml.coreui.feature.ytag.R.drawable.ic_close_20px), + contentDescription = null, + tint = Color.White + ) + } + }) +}