Merge pull request #5197 from element-hq/feature/bma/spaceUiComponent

Space UI component
This commit is contained in:
Benoit Marty 2025-08-21 09:48:22 +02:00 committed by GitHub
commit 0796cf993d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 537 additions and 25 deletions

View file

@ -46,7 +46,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAt
import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule
import io.element.android.libraries.designsystem.atomic.molecules.MembersCountMolecule
import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.Announcement
@ -546,7 +546,7 @@ private fun DefaultLoadedContent(
},
memberCount = {
if (contentState.showMemberCount) {
RoomPreviewMembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0)
MembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0)
}
}
)

View file

@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncIndicator
import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost
import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarRow
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview

View file

@ -25,7 +25,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun RoomPreviewMembersCountMolecule(
fun MembersCountMolecule(
memberCount: Long,
modifier: Modifier = Modifier,
) {
@ -51,13 +51,13 @@ fun RoomPreviewMembersCountMolecule(
@PreviewsDayNight
@Composable
internal fun RoomPreviewMembersCountMoleculePreview() = ElementPreview {
internal fun MembersCountMoleculePreview() = ElementPreview {
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
RoomPreviewMembersCountMolecule(memberCount = 1)
RoomPreviewMembersCountMolecule(memberCount = 888)
RoomPreviewMembersCountMolecule(memberCount = 123_456)
MembersCountMolecule(memberCount = 1)
MembersCountMolecule(memberCount = 888)
MembersCountMolecule(memberCount = 123_456)
}
}

View file

@ -16,8 +16,8 @@ open class AvatarDataProvider : PreviewParameterProvider<AvatarData> {
.map {
sequenceOf(
anAvatarData(size = it),
anAvatarData(size = it).copy(name = null),
anAvatarData(size = it).copy(url = "aUrl"),
anAvatarData(size = it, name = null),
anAvatarData(size = it, url = "aUrl"),
)
}
.flatten()
@ -26,10 +26,12 @@ open class AvatarDataProvider : PreviewParameterProvider<AvatarData> {
fun anAvatarData(
// Let's the id not start with a 'a'.
id: String = "@id_of_alice:server.org",
name: String = "Alice",
name: String? = "Alice",
url: String? = null,
size: AvatarSize = AvatarSize.RoomListItem,
) = AvatarData(
id = id,
name = name,
url = url,
size = size,
)

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.knockrequests.impl.banner
package io.element.android.libraries.designsystem.components.avatar
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
@ -23,10 +23,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.internal.OverlapRatioProvider
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toPx
@ -41,6 +38,7 @@ import kotlinx.collections.immutable.toImmutableList
* @param modifier Jetpack Compose modifier
* @param overlapRatio the overlap ration. When 0f, avatars will render without overlap, when 1f
* only the first avatar will be visible
* @param lastOnTop if true, the last visible avatar will be rendered on top.
*/
@Composable
fun AvatarRow(
@ -48,6 +46,7 @@ fun AvatarRow(
avatarType: AvatarType,
modifier: Modifier = Modifier,
overlapRatio: Float = 0.5f,
lastOnTop: Boolean = false,
) {
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
Box(
@ -57,23 +56,35 @@ fun AvatarRow(
val avatarSize = avatarDataList.firstOrNull()?.size?.dp ?: return
val avatarSizePx = avatarSize.toPx()
avatarDataList
.reversed()
.let {
if (lastOnTop) {
it
} else {
it.reversed()
}
}
.forEachIndexed { index, avatarData ->
val startPadding = if (lastOnTop) {
avatarSize * (1 - overlapRatio) * index
} else {
avatarSize * (1 - overlapRatio) * (lastItemIndex - index)
}
Avatar(
modifier = Modifier
.padding(start = avatarSize * (1 - overlapRatio) * (lastItemIndex - index))
.padding(start = startPadding)
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
// Draw content and clear the pixels for the avatar on the left (right in RTL).
// Draw content and clear the pixels for the avatar on the left (right in RTL) or when lastOnTop is true on
// the right (left in RTL).
drawContent()
val xOffset = if (isRtl) {
size.width - avatarSizePx * (overlapRatio - 0.5f)
} else {
0f + avatarSizePx * (overlapRatio - 0.5f)
}
if (index < lastItemIndex) {
val xOffset = if (isRtl == lastOnTop) {
avatarSizePx * (overlapRatio - 0.5f)
} else {
size.width - avatarSizePx * (overlapRatio - 0.5f)
}
drawCircle(
color = Color.Black,
center = Offset(
@ -104,6 +115,17 @@ internal fun AvatarRowPreview(@PreviewParameter(OverlapRatioProvider::class) ove
}
}
@Composable
@PreviewsDayNight
internal fun AvatarRowLastOnTopPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) {
ElementPreview {
ContentToPreview(
overlapRatio = overlapRatio,
lastOnTop = true,
)
}
}
@Composable
@PreviewsDayNight
internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) {
@ -117,7 +139,25 @@ internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class)
}
@Composable
private fun ContentToPreview(overlapRatio: Float) {
@PreviewsDayNight
internal fun AvatarRowLastOnTopRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) {
CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Rtl,
) {
ElementPreview {
ContentToPreview(
overlapRatio = overlapRatio,
lastOnTop = true,
)
}
}
}
@Composable
private fun ContentToPreview(
overlapRatio: Float,
lastOnTop: Boolean = false,
) {
AvatarRow(
avatarDataList = listOf("A", "B", "C").map {
AvatarData(
@ -128,5 +168,6 @@ private fun ContentToPreview(overlapRatio: Float) {
}.toImmutableList(),
avatarType = AvatarType.User,
overlapRatio = overlapRatio,
lastOnTop = lastOnTop,
)
}

View file

@ -63,4 +63,7 @@ enum class AvatarSize(val dp: Dp) {
DmCreationConfirmation(64.dp),
UserVerification(52.dp),
OrganizationHeader(64.dp),
SpaceMember(24.dp),
}

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.knockrequests.impl.banner
package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.ui.tooling.preview.PreviewParameterProvider

View file

@ -0,0 +1,160 @@
/*
* Copyright 2025 New Vector 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.matrix.ui.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.unit.LayoutDirection
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.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.ui.strings.CommonStrings
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3643-2678&m=dev
*/
@Composable
fun EditableOrgAvatar(
avatarData: AvatarData,
onEdit: () -> Unit,
modifier: Modifier = Modifier,
) {
val actionEdit = stringResource(id = CommonStrings.action_edit)
val description = stringResource(CommonStrings.a11y_avatar)
Box(
modifier = modifier
.width(avatarData.size.dp + 16.dp)
.clearAndSetSemantics {
contentDescription = description
// Note: this does not set the click effect to the whole Box
// when talkback is not enabled
onClick(
label = actionEdit,
action = {
onEdit()
true
}
)
}
) {
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
val editIconRadius = 17.dp.toPx()
val editIconXOffset = 7.dp.toPx()
val editIconYOffset = 15.dp.toPx()
Avatar(
avatarData = avatarData,
avatarType = AvatarType.Space(false),
modifier = Modifier
.align(Alignment.Center)
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
drawContent()
val xOffset = if (isRtl) {
editIconXOffset
} else {
size.width - editIconXOffset
}
drawCircle(
color = Color.Black,
center = Offset(
x = xOffset,
y = size.height - editIconYOffset,
),
radius = editIconRadius,
blendMode = BlendMode.Clear,
)
},
)
Surface(
color = ElementTheme.colors.bgCanvasDefault,
shape = CircleShape,
border = BorderStroke(1.dp, color = ElementTheme.colors.borderInteractiveSecondary),
modifier = Modifier
.clip(CircleShape)
.size(30.dp)
.align(Alignment.BottomEnd)
.clickable(
indication = ripple(),
interactionSource = remember { MutableInteractionSource() },
onClick = onEdit,
),
) {
Icon(
imageVector = CompoundIcons.Edit(),
// Note: keep the context description for the test
contentDescription = stringResource(id = CommonStrings.action_edit),
tint = ElementTheme.colors.iconPrimary,
modifier = Modifier.padding(6.dp)
)
}
}
}
@PreviewsDayNight
@Composable
internal fun EditableOrgAvatarPreview() = ElementPreview {
EditableOrgAvatar(
avatarData = anAvatarData(
url = "anUrl",
size = AvatarSize.OrganizationHeader,
),
onEdit = {},
)
}
@PreviewsDayNight
@Composable
internal fun EditableOrgAvatarRtlPreview() = CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Rtl,
) {
ElementPreview {
EditableOrgAvatar(
avatarData = anAvatarData(
url = "anUrl",
size = AvatarSize.OrganizationHeader,
),
onEdit = {},
)
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright 2025 New Vector 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.matrix.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.ui.strings.CommonPlurals
import io.element.android.libraries.ui.strings.CommonStrings
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3643-2048&m=dev
*/
@Composable
fun OrganizationHeader(
avatarData: AvatarData,
name: String,
numberOfSpaces: Int,
numberOfRooms: Int,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(top = 16.dp, bottom = 24.dp, start = 16.dp, end = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Avatar(
avatarData = avatarData,
avatarType = AvatarType.Space(false),
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = name,
style = ElementTheme.typography.fontHeadingLgBold,
color = ElementTheme.colors.textPrimary,
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.height(12.dp))
val subtitle = stringResource(
id = CommonStrings.screen_space_list_details,
pluralStringResource(CommonPlurals.common_spaces, numberOfSpaces, numberOfSpaces),
pluralStringResource(CommonPlurals.common_rooms, numberOfRooms, numberOfRooms),
)
Text(
text = subtitle,
style = ElementTheme.typography.fontBodyLgRegular,
color = ElementTheme.colors.textSecondary,
textAlign = TextAlign.Center,
)
}
}
@PreviewsDayNight
@Composable
internal fun OrganizationHeaderPreview() = ElementPreview {
OrganizationHeader(
avatarData = anAvatarData(
url = "anUrl",
size = AvatarSize.OrganizationHeader,
),
name = "Space name",
numberOfSpaces = 9,
numberOfRooms = 88,
)
}

View file

@ -0,0 +1,107 @@
/*
* Copyright 2025 New Vector 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.matrix.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.atomic.molecules.MembersCountMolecule
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarRow
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3729-605&m=dev
*/
@Composable
fun SpaceMembersView(
heroes: ImmutableList<MatrixUser>,
numberOfMembers: Long,
modifier: Modifier = Modifier,
) {
if (heroes.isEmpty()) {
MembersCountMolecule(
memberCount = numberOfMembers,
modifier = modifier,
)
} else {
SpaceMembersWithAvatar(
heroes = heroes
.take(3)
.map {
it.getAvatarData(AvatarSize.SpaceMember)
}
.toImmutableList(),
numberOfMembers = numberOfMembers,
modifier = modifier,
)
}
}
@Composable
private fun SpaceMembersWithAvatar(
heroes: ImmutableList<AvatarData>,
numberOfMembers: Long,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
AvatarRow(
avatarDataList = heroes,
avatarType = AvatarType.User,
lastOnTop = true,
)
Text(
text = "$numberOfMembers",
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
)
}
}
@Composable
@PreviewsDayNight
internal fun SpaceMembersViewNoHeroesPreview() = ElementPreview {
SpaceMembersView(
heroes = persistentListOf(),
numberOfMembers = 123,
)
}
@Composable
@PreviewsDayNight
internal fun SpaceMembersViewPreview() = ElementPreview(
drawableFallbackForImages = CommonDrawables.sample_avatar,
) {
SpaceMembersView(
heroes = persistentListOf(
aMatrixUser(id = "@1:d", displayName = "Alice", avatarUrl = "aUrl"),
aMatrixUser(id = "@2:d", displayName = "Bob"),
aMatrixUser(id = "@3:d", displayName = "Charlie", avatarUrl = "aUrl"),
aMatrixUser(id = "@4:d", displayName = "Dave"),
),
numberOfMembers = 123,
)
}

View file

@ -98,6 +98,7 @@ class KonsistPreviewTest {
"SasEmojisPreview",
"SecureBackupSetupViewChangePreview",
"SelectedUserCannotRemovePreview",
"SpaceMembersViewNoHeroesPreview",
"TextComposerAddCaptionPreview",
"TextComposerCaptionPreview",
"TextComposerEditCaptionPreview",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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