Floating toolbar (#6147)

* Use floating toolbar on homepage

* Fix deprecation issue

* Create HorizontalFloatingToolbar wrapper in our components.

* Fix Konsist test.

* Fix compilation issue after rebase.

* Fix lint issue. `floatingActionButton` must be the last parameter.

* Add Preview for the case empty space.

* Fix navigation bar overlapping buttons in empty space view.

* Increase content padding, and apply it to the space tab too.

* Update screenshots

---------

Co-authored-by: chelsea <git@cdhildit.ch>
Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Benoit Marty 2026-02-26 14:54:06 +01:00 committed by GitHub
commit e56c7e1227
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 395 additions and 165 deletions

View file

@ -33,7 +33,6 @@ data class HomeState(
val eventSink: (HomeEvent) -> Unit,
) {
val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected
val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats
val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters
val showNavigationBar = homeSpacesState.canCreateSpaces || homeSpacesState.spaceRooms.isNotEmpty()
}

View file

@ -45,7 +45,12 @@ open class HomeStateProvider : PreviewParameterProvider<HomeState> {
),
) + RoomListStateProvider().values.map {
aHomeState(roomListState = it)
}
} + aHomeState(
currentHomeNavigationBarItem = HomeNavigationBarItem.Spaces,
homeSpacesState = aHomeSpacesState(
spaceRooms = emptyList(),
),
)
}
internal fun aHomeState(

View file

@ -21,19 +21,22 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
@ -58,15 +61,15 @@ import io.element.android.libraries.androidutils.throttler.FirstThrottler
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbar
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbarItem
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbarSeparator
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.NavigationBar
import io.element.android.libraries.designsystem.theme.components.NavigationBarIcon
import io.element.android.libraries.designsystem.theme.components.NavigationBarItem
import io.element.android.libraries.designsystem.theme.components.NavigationBarText
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
@Composable
@ -185,12 +188,10 @@ private fun HomeScaffold(
onAccountSwitch = {
state.eventSink(HomeEvent.SwitchToAccount(it))
},
onCreateSpace = onCreateSpaceClick,
scrollBehavior = scrollBehavior,
displayFilters = state.displayRoomListFilters,
filtersState = roomListState.filtersState,
spaceFiltersState = roomListState.spaceFiltersState,
canCreateSpaces = state.homeSpacesState.canCreateSpaces,
canReportBug = state.canReportBug,
modifier = Modifier.hazeEffect(
state = hazeState,
@ -198,7 +199,7 @@ private fun HomeScaffold(
)
)
},
bottomBar = {
floatingActionButton = {
if (state.showNavigationBar) {
val coroutineScope = rememberCoroutineScope()
HomeBottomBar(
@ -222,14 +223,29 @@ private fun HomeScaffold(
state.eventSink(HomeEvent.SelectHomeNavigationBarItem(item))
}
},
modifier = Modifier.hazeEffect(
state = hazeState,
style = HazeMaterials.thick(),
)
floatingActionButton = when (state.currentHomeNavigationBarItem) {
HomeNavigationBarItem.Chats -> {
{
HomeFloatingActionButton(onStartChatClick, CommonStrings.action_create_room)
}
}
HomeNavigationBarItem.Spaces -> if (state.homeSpacesState.canCreateSpaces) {
{
HomeFloatingActionButton(onCreateSpaceClick, CommonStrings.action_create_space)
}
} else {
// No FAB for spaces if we cannot create spaces
null
}
},
)
}
},
floatingActionButtonPosition = FabPosition.Center,
content = { padding ->
val contentPadding = PaddingValues(
bottom = 112.dp,
)
when (state.currentHomeNavigationBarItem) {
HomeNavigationBarItem.Chats -> {
RoomListContentView(
@ -243,15 +259,7 @@ private fun HomeScaffold(
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
onRoomClick = ::onRoomClick,
onCreateRoomClick = onStartChatClick,
contentPadding = PaddingValues(
// FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80,
// and include provided bottom padding
// Disable contentPadding due to navigation issue using the keyboard
// See https://issuetracker.google.com/issues/436432313
bottom = 80.dp,
// bottom = 80.dp + padding.calculateBottomPadding(),
// top = padding.calculateTopPadding()
),
contentPadding = contentPadding,
modifier = Modifier
.padding(
PaddingValues(
@ -274,6 +282,7 @@ private fun HomeScaffold(
.padding(padding)
.consumeWindowInsets(padding)
.hazeSource(state = hazeState),
contentPadding = contentPadding,
state = state.homeSpacesState,
lazyListState = spacesLazyListState,
onSpaceClick = { spaceId ->
@ -286,49 +295,48 @@ private fun HomeScaffold(
}
}
},
floatingActionButton = {
if (state.displayActions) {
FloatingActionButton(
onClick = onStartChatClick,
) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message),
)
}
}
},
snackbarHost = { SnackbarHost(snackbarHostState) },
)
}
@Composable
private fun HomeFloatingActionButton(
onClick: () -> Unit,
contentDescription: Int,
modifier: Modifier = Modifier,
) {
FloatingActionButton(onClick = onClick, modifier = modifier) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = stringResource(id = contentDescription),
)
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun HomeBottomBar(
currentHomeNavigationBarItem: HomeNavigationBarItem,
onItemClick: (HomeNavigationBarItem) -> Unit,
modifier: Modifier = Modifier,
floatingActionButton: (@Composable () -> Unit)?,
) {
NavigationBar(
containerColor = Color.Transparent,
HorizontalFloatingToolbar(
floatingActionButton = floatingActionButton,
modifier = modifier
.padding(bottom = ScreenOffset)
.zIndex(1f),
) {
HomeNavigationBarItem.entries.forEach { item ->
HomeNavigationBarItem.entries.forEachIndexed { index, item ->
if (index > 0) {
HorizontalFloatingToolbarSeparator()
}
val isSelected = currentHomeNavigationBarItem == item
NavigationBarItem(
selected = isSelected,
onClick = {
onItemClick(item)
},
icon = {
NavigationBarIcon(
imageVector = item.icon(isSelected),
)
},
label = {
NavigationBarText(
text = stringResource(item.labelRes),
)
}
HorizontalFloatingToolbarItem(
icon = item.icon(isSelected),
tooltipLabel = stringResource(item.labelRes),
isSelected = isSelected,
onClick = { onItemClick(item) },
)
}
}

View file

@ -87,9 +87,7 @@ fun HomeTopBar(
onMenuActionClick: (RoomListMenuAction) -> Unit,
onOpenSettings: () -> Unit,
onAccountSwitch: (SessionId) -> Unit,
onCreateSpace: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior,
canCreateSpaces: Boolean,
canReportBug: Boolean,
displayFilters: Boolean,
filtersState: RoomListFiltersState,
@ -134,17 +132,13 @@ fun HomeTopBar(
)
},
actions = {
when (selectedNavigationItem) {
HomeNavigationBarItem.Chats -> RoomListMenuItems(
if (selectedNavigationItem == HomeNavigationBarItem.Chats) {
RoomListMenuItems(
onToggleSearch = onToggleSearch,
onMenuActionClick = onMenuActionClick,
canReportBug = canReportBug,
spaceFiltersState = spaceFiltersState,
)
HomeNavigationBarItem.Spaces -> SpacesMenuItems(
canCreateSpaces = canCreateSpaces,
onCreateSpace = onCreateSpace
)
}
},
// We want a 16dp left padding for the navigationIcon :
@ -230,21 +224,6 @@ private fun RoomListMenuItems(
}
}
@Composable
private fun SpacesMenuItems(
canCreateSpaces: Boolean,
onCreateSpace: () -> Unit
) {
if (canCreateSpaces) {
IconButton(onClick = onCreateSpace) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = stringResource(CommonStrings.action_create_space)
)
}
}
}
@Composable
private fun SpaceFilterButton(
spaceFiltersState: SpaceFiltersState,
@ -365,8 +344,6 @@ internal fun HomeTopBarPreview() = ElementPreview {
onOpenSettings = {},
onAccountSwitch = {},
onToggleSearch = {},
onCreateSpace = {},
canCreateSpaces = true,
canReportBug = true,
displayFilters = true,
filtersState = aRoomListFiltersState(),
@ -388,8 +365,6 @@ internal fun HomeTopBarSpaceFiltersSelectedPreview() = ElementPreview {
onOpenSettings = {},
onAccountSwitch = {},
onToggleSearch = {},
onCreateSpace = {},
canCreateSpaces = true,
canReportBug = true,
displayFilters = true,
filtersState = aRoomListFiltersState(),
@ -411,8 +386,6 @@ internal fun HomeTopBarSpacesPreview() = ElementPreview {
onOpenSettings = {},
onAccountSwitch = {},
onToggleSearch = {},
onCreateSpace = {},
canCreateSpaces = true,
canReportBug = true,
displayFilters = false,
filtersState = aRoomListFiltersState(),
@ -434,8 +407,6 @@ internal fun HomeTopBarWithIndicatorPreview() = ElementPreview {
onOpenSettings = {},
onAccountSwitch = {},
onToggleSearch = {},
onCreateSpace = {},
canCreateSpaces = true,
canReportBug = true,
displayFilters = true,
filtersState = aRoomListFiltersState(),
@ -457,8 +428,6 @@ internal fun HomeTopBarMultiAccountPreview() = ElementPreview {
onOpenSettings = {},
onAccountSwitch = {},
onToggleSearch = {},
onCreateSpace = {},
canCreateSpaces = true,
canReportBug = true,
displayFilters = true,
filtersState = aRoomListFiltersState(),

View file

@ -10,6 +10,7 @@ package io.element.android.features.home.impl.spaces
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@ -48,6 +49,7 @@ import kotlinx.collections.immutable.toImmutableList
fun HomeSpacesView(
state: HomeSpacesState,
lazyListState: LazyListState,
contentPadding: PaddingValues,
onSpaceClick: (RoomId) -> Unit,
onCreateSpaceClick: () -> Unit,
onExploreClick: () -> Unit,
@ -55,7 +57,7 @@ fun HomeSpacesView(
) {
if (state.canCreateSpaces && state.spaceRooms.isEmpty()) {
EmptySpaceHomeView(
modifier = modifier,
modifier = modifier.padding(contentPadding),
onCreateSpaceClick = onCreateSpaceClick,
onExploreClick = onExploreClick,
canExploreSpaces = state.canExploreSpaces,
@ -63,7 +65,8 @@ fun HomeSpacesView(
} else {
LazyColumn(
modifier = modifier,
state = lazyListState
state = lazyListState,
contentPadding = contentPadding,
) {
val space = state.space
when (space) {
@ -115,6 +118,9 @@ fun HomeSpacesView(
}
}
/**
* Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=1763-74215&t=9IGKMXHDfTGAqzQK-4
*/
@Composable
private fun EmptySpaceHomeView(
onCreateSpaceClick: () -> Unit,
@ -159,8 +165,7 @@ private fun EmptySpaceHomeView(
}
}
}
) {
}
)
}
@PreviewsDayNight
@ -174,5 +179,6 @@ internal fun HomeSpacesViewPreview(
onSpaceClick = {},
onCreateSpaceClick = {},
onExploreClick = {},
contentPadding = PaddingValues(bottom = 112.dp),
)
}

View file

@ -120,7 +120,7 @@ androidx_preference = "androidx.preference:preference:1.2.1"
androidx_webkit = "androidx.webkit:webkit:1.15.0"
androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" }
androidx_compose_material3 = { module = "androidx.compose.material3:material3" }
androidx_compose_material3 = { module = "androidx.compose.material3:material3", version = '1.5.0-alpha11' }
androidx_compose_material3_windowsizeclass = { module = "androidx.compose.material3:material3-window-size-class" }
androidx_compose_material3_adaptive = "androidx.compose.material3:material3-adaptive-android:1.0.0-alpha06"
androidx_compose_ui = { module = "androidx.compose.ui:ui" }

View file

@ -0,0 +1,223 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.FloatingToolbarColors
import androidx.compose.material3.FloatingToolbarDefaults
import androidx.compose.material3.FloatingToolbarHorizontalFabPosition
import androidx.compose.material3.FloatingToolbarScrollBehavior
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.rememberTooltipState
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.Shape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.atomic.atoms.CounterAtom
import io.element.android.libraries.designsystem.components.tooltip.PlainTooltip
import io.element.android.libraries.designsystem.components.tooltip.TooltipBox
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=4457-1136
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
fun HorizontalFloatingToolbar(
modifier: Modifier = Modifier,
expanded: Boolean = true,
floatingActionButton: (@Composable () -> Unit)? = null,
colors: FloatingToolbarColors = FloatingToolbarDefaults.standardFloatingToolbarColors().copy(
toolbarContainerColor = ElementTheme.colors.bgSubtleSecondary,
),
contentPadding: PaddingValues = PaddingValues(
vertical = 8.dp,
horizontal = 12.dp,
),
scrollBehavior: FloatingToolbarScrollBehavior? = null,
shape: Shape = FloatingToolbarDefaults.ContainerShape,
leadingContent: @Composable (RowScope.() -> Unit)? = null,
trailingContent: @Composable (RowScope.() -> Unit)? = null,
floatingActionButtonPosition: FloatingToolbarHorizontalFabPosition =
FloatingToolbarHorizontalFabPosition.End,
animationSpec: FiniteAnimationSpec<Float> = FloatingToolbarDefaults.animationSpec(),
expandedShadowElevation: Dp = 8.dp,
collapsedShadowElevation: Dp = if (floatingActionButton == null) {
FloatingToolbarDefaults.ContainerCollapsedElevation
} else {
FloatingToolbarDefaults.ContainerCollapsedElevationWithFab
},
content: @Composable RowScope.() -> Unit,
) {
if (floatingActionButton == null) {
androidx.compose.material3.HorizontalFloatingToolbar(
expanded = expanded,
modifier = modifier,
colors = colors,
contentPadding = contentPadding,
scrollBehavior = scrollBehavior,
shape = shape,
leadingContent = leadingContent,
trailingContent = trailingContent,
expandedShadowElevation = expandedShadowElevation,
collapsedShadowElevation = collapsedShadowElevation,
content = content,
)
} else {
androidx.compose.material3.HorizontalFloatingToolbar(
expanded = expanded,
floatingActionButton = floatingActionButton,
modifier = modifier,
colors = colors,
contentPadding = contentPadding,
scrollBehavior = scrollBehavior,
shape = shape,
floatingActionButtonPosition = floatingActionButtonPosition,
animationSpec = animationSpec,
expandedShadowElevation = expandedShadowElevation,
collapsedShadowElevation = collapsedShadowElevation,
content = content,
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HorizontalFloatingToolbarItem(
icon: ImageVector,
tooltipLabel: String,
isSelected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
counter: Int? = null,
forceRenderingTooltip: Boolean = false,
) {
TooltipBox(
positionProvider =
TooltipDefaults.rememberTooltipPositionProvider(
TooltipAnchorPosition.Above
),
tooltip = { PlainTooltip { Text(tooltipLabel) } },
state = rememberTooltipState(
initialIsVisible = forceRenderingTooltip,
),
modifier = modifier,
) {
val colors = if (isSelected) {
IconButtonDefaults.filledIconButtonColors().copy(
containerColor = ElementTheme.colors.bgCanvasDefault,
contentColor = ElementTheme.colors.iconPrimary,
)
} else {
IconButtonDefaults.filledIconButtonColors().copy(
containerColor = Color.Transparent,
contentColor = ElementTheme.colors.iconSecondary,
)
}
Box {
FilledIconButton(
modifier = Modifier.widthIn(min = 56.dp),
colors = colors,
onClick = onClick,
) {
Icon(
modifier = Modifier.size(24.dp),
imageVector = icon,
contentDescription = tooltipLabel,
)
}
if (counter != null) {
CounterAtom(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(top = 6.dp, end = 3.dp),
count = counter,
textStyle = ElementTheme.typography.fontBodyXsMedium,
)
}
}
}
}
@Composable
fun HorizontalFloatingToolbarSeparator(modifier: Modifier = Modifier) {
Spacer(modifier = modifier.width(16.dp))
}
@PreviewsDayNight
@Composable
internal fun HorizontalFloatingToolbarPreview() = ElementPreview {
ContentToPreview(
floatingActionButton = {
FloatingActionButton(
onClick = {},
) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = null,
)
}
}
)
}
@PreviewsDayNight
@Composable
internal fun HorizontalFloatingToolbarNoFabPreview() = ElementPreview {
ContentToPreview(
floatingActionButton = null,
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview(
floatingActionButton: (@Composable () -> Unit)?,
) {
HorizontalFloatingToolbar(
modifier = Modifier.padding(28.dp),
floatingActionButton = floatingActionButton,
) {
listOf(
CompoundIcons.ChatSolid(),
CompoundIcons.Space(),
).forEachIndexed { index, icon ->
if (index > 0) {
HorizontalFloatingToolbarSeparator()
}
HorizontalFloatingToolbarItem(
icon = icon,
tooltipLabel = "Label",
isSelected = index == 0,
counter = if (index == 0) 6 else null,
forceRenderingTooltip = true,
onClick = { },
)
}
}
}

View file

@ -107,10 +107,10 @@ fun ListItem(
onClick: (() -> Unit)? = null,
) {
// We cannot just pass the disabled colors, they must be set manually: https://issuetracker.google.com/issues/280480132
val headlineColor = if (enabled) colors.headlineColor else colors.disabledHeadlineColor
val supportingColor = if (enabled) colors.supportingTextColor else colors.disabledHeadlineColor.copy(alpha = 0.80f)
val leadingContentColor = if (enabled) colors.leadingIconColor else colors.disabledLeadingIconColor
val trailingContentColor = if (enabled) colors.trailingIconColor else colors.disabledTrailingIconColor
val headlineColor = if (enabled) colors.contentColor else colors.disabledContentColor
val supportingColor = if (enabled) colors.supportingContentColor else colors.disabledContentColor.copy(alpha = 0.80f)
val leadingContentColor = if (enabled) colors.leadingContentColor else colors.disabledLeadingContentColor
val trailingContentColor = if (enabled) colors.trailingContentColor else colors.disabledTrailingContentColor
val decoratedHeadlineContent: @Composable () -> Unit = {
CompositionLocalProvider(

View file

@ -47,6 +47,8 @@ class KonsistComposableTest {
"CompoundSemanticColorsLightHc",
"CompoundSemanticColorsDark",
"CompoundSemanticColorsDarkHc",
"HorizontalFloatingToolbarItem",
"HorizontalFloatingToolbarSeparator",
)
.assertTrue(
additionalMessage =

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf07c13a8f8b7da568e59441c7597de071f5d4ca728930a7cfba67500d981efd
size 12334
oid sha256:499d48e33c188e42e066e30644d2ce4d1a2516b6b26d15c385d613234777d738
size 12105

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81bfe2cfe21bb21a41579e0cf81e020997de0c0f12b66b6db172d71120f72b90
size 11420
oid sha256:1f9d7c7900c41fbc10b27a68bf079e4750c28cfdcc4d96db35a79f3606cd5796
size 11205

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:711645c8cd9bcd04105d2f0d1cf37376691d574340b815e5d9b98e9ecc8b8b23
size 85538
oid sha256:f3ac57efcab393ce13794dde2ad7e0d60757c0ef7fd0b09ff0a3c338de9a9edd
size 85467

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:67a160a71f24d46778cf03d7011d98ac6094b6efd1ba47845c4c36114fcec932
size 38969
oid sha256:f0567c1fe44efb4f9e188252a6840c228eb03e8d7954c95b79c693ac31451cd2
size 38840

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:67a160a71f24d46778cf03d7011d98ac6094b6efd1ba47845c4c36114fcec932
size 38969
oid sha256:f0567c1fe44efb4f9e188252a6840c228eb03e8d7954c95b79c693ac31451cd2
size 38840

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30b3ee70ad7dbd0b4f2db3b00b1edacecbbf3e2370798c60dc7696fe75cc843d
size 24704
oid sha256:ecd79d3f8c4efbb54efa6a5b8dee79274ae9957b01146025dab42a45fc328fc0
size 24740

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:45296f7e74f888e3b84f56f45b0d926970363013704ceb62df2afa132f81cbd5
size 83671
oid sha256:0f8a54913bd0c207088cec3c0fff9bcab4dca084884493a32a615fa728c40625
size 83659

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b9b9a37c8ed9650d16f498c3f00a182a83dc53892cbc8558c30138e0de56ad94
size 37669
oid sha256:949b1843a373b86fde0bb5a0a0637f938e87aeb3d36b8c146052e99874dfb4dd
size 37667

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b9b9a37c8ed9650d16f498c3f00a182a83dc53892cbc8558c30138e0de56ad94
size 37669
oid sha256:949b1843a373b86fde0bb5a0a0637f938e87aeb3d36b8c146052e99874dfb4dd
size 37667

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b4649d1e05ab3b05d3bd730bc69a5e4398a0cb024027e9720be3bee2f49a4d01
size 23757
oid sha256:0fd37517b8913e2bac3275f717aa15bed18055a7525e0404b25b43cb8bd8422d
size 23840

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:86696c9ef71c5cf5edddf908e477d1e392fc9b3334a390df0fd2c2419c703de1
size 142152
oid sha256:c4bd92f12e7926f39cf7bb74fedd85b8295bff34da08e45229e2e1a8f83956c3
size 144306

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc87652e8e0ab0a215ca08ad64def6eb59a3c44a66af0f837cbeb7e1fd06701d
size 65071
oid sha256:5f106b4672b1d96665fe731de28b7ccbb98f986580fd95cc6609a7b64348c60e
size 67665

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bdf23256bdc64325fab24411b39abc9fa62ced7f0bda1b24a4df387917064df
size 33352
oid sha256:763715d657faecd68e201b7a110d022c04e27592bff499355662848455e2adc3
size 36373

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6eca672ad59f17058a552f712ce0fb27cca807bf2fcd1a96962fbf799861b5d3
size 28046
oid sha256:b0ecffc6b05c1bc3d0c6ba578d7ca7d50ac9c7a4948c53fe96d427c3ad6b4e4e
size 32289

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:583da21c83fff6ec3e81c3602b2cb8c1c3e5753f32892ab7ae70633ef6cf59e9
size 83978
oid sha256:b659de7521274ae555f962639d7e1652a22958c4b066dae3f2dd1aa70d93b78e
size 89194

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ca16a8b022e217c954b322f463bf4cad125eed488a67d64712aefe04276a34e
size 82657
oid sha256:bd11e404d07f8d790b1554c760f24e4e82614a331e16219c67c326b0375313f9
size 84336

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5f1c19e5c138ba9b4573041c11e771b74c6916faf7fec505cd9d545556d1583
size 51083
oid sha256:5406580880491251a8d61fc2fe26dab90153e6337f95dae3d6e4b1c5df7ac8f4
size 53764

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:750a7b57fd2fd99f3cd0ad6e970692cdd8a7ad191dfb8fff0b5d0935461789db
size 42594

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc87652e8e0ab0a215ca08ad64def6eb59a3c44a66af0f837cbeb7e1fd06701d
size 65071
oid sha256:5f106b4672b1d96665fe731de28b7ccbb98f986580fd95cc6609a7b64348c60e
size 67665

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc87652e8e0ab0a215ca08ad64def6eb59a3c44a66af0f837cbeb7e1fd06701d
size 65071
oid sha256:5f106b4672b1d96665fe731de28b7ccbb98f986580fd95cc6609a7b64348c60e
size 67665

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11e49c75d058dde6b661ed69684b679b0a86832e1f51e097ed96df03af29ef3f
size 61748
oid sha256:f8bdb8d3e2dbb468115c4077f5bce6415a7708cbd04f825225139c1a2cbf788d
size 64647

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d31d6ae0f15ca0b2db8425cdd0588ee1be0ccae543588be32b34dda5d9d4246f
size 52454
oid sha256:1ed6905e2833c7e927c84f0407a6218a4859a28f5a36a34c3a9b92f66347308a
size 56117

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc87652e8e0ab0a215ca08ad64def6eb59a3c44a66af0f837cbeb7e1fd06701d
size 65071
oid sha256:5f106b4672b1d96665fe731de28b7ccbb98f986580fd95cc6609a7b64348c60e
size 67665

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ca4ab313876d1c530d0829433b6a2c998afc4e761a1061364e3dbb457487646
size 82515
oid sha256:521e3b0b36dbed830eab8cd884a1924381bd9a50c189ff9ef074ed468aaf9b23
size 84195

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5dd233a5b5a17dfcdd16f70c354d33e74b6eb24180cc2235b41019684a1ab7fc
size 61852
oid sha256:00af046190654a77e02d1152e8d44f538302b2fecfbd31b8790cdfa6f202dc1b
size 62502

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ae78be15862aef18f0fe889c4df469cc7a3d77ebffbcf09c4b4c7baebb177e2f
size 30332
oid sha256:86305c47144ed620efd27045239c0ca66f11cb5f2088b4df8e7f9e7a254576fa
size 31166

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ab5ddcdbb507c6c0953cd8b3bbddb73d9e98733d8edee5be37e012275a594db1
size 24749
oid sha256:cee0eefc4639ede46fd6539da213f0398de0611fc1e6f2002c6e014318c04380
size 26665

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5fc942ea686b3a58d930266a5cc7a9f663ff634512397be6f06850956f419b1e
size 79978
oid sha256:1605040770a4b5d8928b41afab2e2f970f18283942659b2b869451e5c04d3d5b
size 83224

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:74ac553cb7d2bc0883cc1cc5f12d692dcaa63df6c1f6d84cf6e5cb099ad15159
size 78750
oid sha256:90bf5140e04bd22af001ed9d23513b26d555f2d73864d9012c8e05f452659fd8
size 78641

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:03ed724447496e01989773821c20594325c738389f61fd2af162e2d7d82b2651
size 47386
oid sha256:828d2d972c03e2cdb837e591c957c93fd61c1f49e3e4fcf19a8bf18963d4eb95
size 48307

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b1d6080fd17d90e63b4aae91c6a2c93bd16314eacc6007f57073e981c4dfb1c6
size 37378

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5dd233a5b5a17dfcdd16f70c354d33e74b6eb24180cc2235b41019684a1ab7fc
size 61852
oid sha256:00af046190654a77e02d1152e8d44f538302b2fecfbd31b8790cdfa6f202dc1b
size 62502

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5dd233a5b5a17dfcdd16f70c354d33e74b6eb24180cc2235b41019684a1ab7fc
size 61852
oid sha256:00af046190654a77e02d1152e8d44f538302b2fecfbd31b8790cdfa6f202dc1b
size 62502

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3c966a3f7d1fe4ba6e1fe73ca5fad71ef818433bc5fc35cc77b537f875dde0af
size 58907
oid sha256:71e33566c0404f1930831b846a2e78952dd33138ffde5257c3d413739e225040
size 60212

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c889d00a2dedf3f6ad596a819709daf9d6537314679a72143efbc2932d687da
size 50622
oid sha256:363efc8054e6a1e04d0482e5351d7e190febf55167a0358d4251101ee6ebd659
size 50942

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5dd233a5b5a17dfcdd16f70c354d33e74b6eb24180cc2235b41019684a1ab7fc
size 61852
oid sha256:00af046190654a77e02d1152e8d44f538302b2fecfbd31b8790cdfa6f202dc1b
size 62502

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f4dbf6e03444e3d28756abd6985be2f394317cc9e982a0b059f10fdc2d562d50
size 78607
oid sha256:465ffe0ea53fe7bde74c9dddb1be5e9f2861436b5292956f889dcf60db621af2
size 78499

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:75de341f705bb22955f9399180519b65ad59c2e1917317d56e50ea4db78f35bc
size 12102

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f
size 9838

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:16dee1dfe746cd40dac9fe8f0af3ecb42cd14bab5cc490917b589beed065ed1b
size 14686

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d57df7110371c9224e232b707d6a1b10ce14afcb56c803a78220792f2a0d4cf2
size 11354