Bug 1814994 - Create Tabs Tray list and grid layouts

fenix/113.0
Noah Bond 1 year ago committed by mergify[bot]
parent fd61f88da2
commit 0acebea167

@ -12,29 +12,39 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.unit.dp
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.rememberPagerState
import kotlinx.coroutines.launch
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.compose.Divider
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
/**
* Top-level UI for displaying the Tabs Tray feature.
*
* @param tabsTrayStore [TabsTrayStore] used to listen for changes to [TabsTrayState].
* @param displayTabsInGrid Whether the normal and private tabs should be displayed in a grid.
*/
@OptIn(ExperimentalPagerApi::class)
@OptIn(ExperimentalPagerApi::class, ExperimentalComposeUiApi::class)
@Composable
fun TabsTray(
tabsTrayStore: TabsTrayStore,
displayTabsInGrid: Boolean,
) {
val multiselectMode = tabsTrayStore
.observeAsComposableState { state -> state.mode }.value ?: TabsTrayState.Mode.Normal
val normalTabs = tabsTrayStore
.observeAsComposableState { state -> state.normalTabs }.value ?: emptyList()
val pagerState = rememberPagerState(initialPage = 0)
val scope = rememberCoroutineScope()
val animateScrollToPage: ((Page) -> Unit) = { page ->
@ -48,10 +58,12 @@ fun TabsTray(
.fillMaxSize()
.background(FirefoxTheme.colors.layer1),
) {
TabsTrayBanner(
isInMultiSelectMode = multiselectMode is TabsTrayState.Mode.Select,
onTabPageIndicatorClicked = animateScrollToPage,
)
Box(modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
TabsTrayBanner(
isInMultiSelectMode = multiselectMode is TabsTrayState.Mode.Select,
onTabPageIndicatorClicked = animateScrollToPage,
)
}
Divider()
@ -64,12 +76,17 @@ fun TabsTray(
) { position ->
when (Page.positionToPage(position)) {
Page.NormalTabs -> {
Text(
text = "Normal tabs",
modifier = Modifier.padding(all = 16.dp),
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.body1,
)
FirefoxTheme(theme = Theme.getTheme(allowPrivateTheme = false)) {
if (displayTabsInGrid) {
TabGrid(
tabs = normalTabs,
)
} else {
TabList(
tabs = normalTabs,
)
}
}
}
Page.PrivateTabs -> {
Text(
@ -96,9 +113,16 @@ fun TabsTray(
@LightDarkPreview
@Composable
private fun TabsTrayPreview() {
val store = TabsTrayStore(
initialState = TabsTrayState(
normalTabs = generateFakeTabsList(),
),
)
FirefoxTheme {
TabsTray(
tabsTrayStore = TabsTrayStore(),
tabsTrayStore = store,
displayTabsInGrid = false,
)
}
}
@ -109,12 +133,25 @@ private fun TabsTrayMultiSelectPreview() {
val store = TabsTrayStore(
initialState = TabsTrayState(
mode = TabsTrayState.Mode.Select(setOf()),
normalTabs = generateFakeTabsList(),
),
)
FirefoxTheme {
TabsTray(
tabsTrayStore = store,
displayTabsInGrid = true,
)
}
}
private fun generateFakeTabsList(tabCount: Int = 10): List<TabSessionState> {
val fakeTab = TabSessionState(
id = "tabId",
content = ContentState(
url = "www.mozilla.com",
),
)
return List(tabCount) { fakeTab }
}

@ -208,6 +208,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
FirefoxTheme {
TabsTray(
tabsTrayStore = tabsTrayStore,
displayTabsInGrid = requireContext().settings().gridTabView,
)
}
}

@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.compose.tabstray.TabGridItem
import org.mozilla.fenix.compose.tabstray.TabListItem
import org.mozilla.fenix.tabstray.ext.MIN_COLUMN_WIDTH_DP
import org.mozilla.fenix.theme.FirefoxTheme
/**
* Top-level UI for displaying a list of tabs.
*
* @param tabs The list of [TabSessionState] to display.
*/
@Composable
fun TabList(
tabs: List<TabSessionState>,
) {
val tabListBottomPadding = dimensionResource(id = R.dimen.tab_tray_list_bottom_padding)
val state = rememberLazyListState()
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = state,
) {
items(
items = tabs,
key = { tab -> tab.id },
) { tab ->
TabListItem(
tab = tab,
onCloseClick = {},
onMediaClick = {},
onClick = {},
onLongClick = {},
)
}
item {
Spacer(modifier = Modifier.height(tabListBottomPadding))
}
}
}
/**
* Top-level UI for displaying a grid of tabs.
*
* @param tabs The list of [TabSessionState] to display.
*/
@Composable
fun TabGrid(
tabs: List<TabSessionState>,
) {
val tabListBottomPadding = dimensionResource(id = R.dimen.tab_tray_list_bottom_padding)
val state = rememberLazyGridState()
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = MIN_COLUMN_WIDTH_DP.dp),
modifier = Modifier.fillMaxSize(),
state = state,
) {
items(
items = tabs,
key = { tab -> tab.id },
) { tab ->
TabGridItem(
tab = tab,
onCloseClick = {},
onMediaClick = {},
onClick = {},
onLongClick = {},
)
}
item(span = { GridItemSpan(maxLineSpan) }) {
Spacer(modifier = Modifier.height(tabListBottomPadding))
}
}
}
@LightDarkPreview
@Composable
private fun TabListPreview() {
FirefoxTheme {
Box(
modifier = Modifier
.fillMaxSize()
.background(FirefoxTheme.colors.layer1),
) {
TabList(
tabs = generateFakeTabsList(),
)
}
}
}
@LightDarkPreview
@Composable
private fun TabGridPreview() {
FirefoxTheme {
Box(
modifier = Modifier
.fillMaxSize()
.background(FirefoxTheme.colors.layer1),
) {
TabGrid(
tabs = generateFakeTabsList(),
)
}
}
}
private fun generateFakeTabsList(tabCount: Int = 10): List<TabSessionState> {
val fakeTab = TabSessionState(
id = "tabId",
content = ContentState(
url = "www.mozilla.com",
),
)
return List(tabCount) { fakeTab }
}
Loading…
Cancel
Save