This commit is contained in:
NamanPandey 2023-01-19 13:59:35 +05:30 коммит произвёл GitHub
Родитель b14bc57486
Коммит 6c7af89368
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 1190 добавлений и 299 удалений

Просмотреть файл

@ -28,6 +28,7 @@
<!-- Demo activities -->
<activity android:name="com.microsoft.fluentuidemo.demos.ActionBarLayoutActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.V2AppBarLayoutActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.AppBarLayoutActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.AvatarViewActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.AvatarGroupViewActivity" />

Просмотреть файл

@ -11,6 +11,7 @@ import kotlin.reflect.KClass
const val ACTION_BAR_LAYOUT = "ActionBarLayout"
const val APP_BAR_LAYOUT = "AppBarLayout"
const val V2APP_BAR_LAYOUT = "V2 AppBarLayout"
const val AVATAR_VIEW = "AvatarView"
const val AVATAR_GROUP_VIEW = "AvatarGroupView"
const val BASIC_INPUTS = "Basic Inputs"
@ -50,6 +51,7 @@ const val TOOLTIP = "Tooltip"
const val TYPOGRAPHY = "Typography"
val DEMOS = arrayListOf(
Demo(V2APP_BAR_LAYOUT, V2AppBarLayoutActivity::class),
Demo(V2AVATAR, V2AvatarActivity::class),
Demo(V2AVATAR_CAROUSEL, V2AvatarCarouselActivity::class),
Demo(V2AVATAR_GROUP, V2AvatarGroupActivity::class),

Просмотреть файл

@ -0,0 +1,352 @@
package com.microsoft.fluentuidemo.demos
import android.os.Bundle
import android.widget.Toast
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Email
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.icons.ListItemIcons
import com.microsoft.fluentui.icons.SearchBarIcons
import com.microsoft.fluentui.icons.listitemicons.Chevron
import com.microsoft.fluentui.icons.searchbaricons.Arrowback
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.ThemeMode
import com.microsoft.fluentui.theme.token.AliasTokens
import com.microsoft.fluentui.theme.token.FluentColor
import com.microsoft.fluentui.theme.token.FluentIcon
import com.microsoft.fluentui.theme.token.FluentStyle
import com.microsoft.fluentui.theme.token.controlTokens.AppBarSize
import com.microsoft.fluentui.theme.token.controlTokens.AvatarSize
import com.microsoft.fluentui.theme.token.controlTokens.AvatarStatus
import com.microsoft.fluentui.tokenized.AppBar
import com.microsoft.fluentui.tokenized.SearchBar
import com.microsoft.fluentui.tokenized.controls.ToggleSwitch
import com.microsoft.fluentui.tokenized.listitem.ChevronOrientation
import com.microsoft.fluentui.tokenized.listitem.ListItem
import com.microsoft.fluentui.tokenized.persona.Avatar
import com.microsoft.fluentui.tokenized.persona.Person
import com.microsoft.fluentui.tokenized.segmentedcontrols.PillBar
import com.microsoft.fluentui.tokenized.segmentedcontrols.PillMetaData
import com.microsoft.fluentuidemo.DemoActivity
import com.microsoft.fluentuidemo.R
import kotlin.math.max
class V2AppBarLayoutActivity : DemoActivity() {
override val contentLayoutId: Int
get() = R.layout.v2_activity_compose
override val contentNeedsScrollableContainer: Boolean
get() = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val composeHere = findViewById<ComposeView>(R.id.compose_here)
val context = this
composeHere.setContent {
FluentTheme {
var style: FluentStyle by rememberSaveable { mutableStateOf(FluentStyle.Neutral) }
var appBarSize: AppBarSize by rememberSaveable { mutableStateOf(AppBarSize.Small) }
var searchMode: Boolean by rememberSaveable { mutableStateOf(false) }
var subtitle: String? by rememberSaveable { mutableStateOf("Subtitle") }
var enableSearchBar: Boolean by rememberSaveable { mutableStateOf(false) }
var enableButtonBar: Boolean by rememberSaveable { mutableStateOf(false) }
var yAxisDelta: Float by rememberSaveable { mutableStateOf(1.0F) }
Column(modifier = Modifier.pointerInput(Unit) {
detectDragGestures { _, distance ->
if (searchMode)
yAxisDelta = 0F
else
yAxisDelta = max(0F, distance.y + 10F) / 20F
}
}) {
ListItem.SectionHeader(
title = LocalContext.current.resources.getString(R.string.app_modifiable_parameters),
enableChevron = true,
enableContentOpenCloseTransition = true,
chevronOrientation = ChevronOrientation(90f, 0f),
) {
Column {
PillBar(
mutableListOf(
PillMetaData(
text = LocalContext.current.resources.getString(R.string.fluentui_large),
onClick = { appBarSize = AppBarSize.Large },
selected = appBarSize == AppBarSize.Large
),
PillMetaData(
text = LocalContext.current.resources.getString(R.string.fluentui_medium),
onClick = { appBarSize = AppBarSize.Medium },
selected = appBarSize == AppBarSize.Medium
),
PillMetaData(
text = LocalContext.current.resources.getString(R.string.fluentui_small),
onClick = { appBarSize = AppBarSize.Small },
selected = appBarSize == AppBarSize.Small
),
PillMetaData(
text = LocalContext.current.resources.getString(R.string.fluentui_search),
onClick = { searchMode = !searchMode },
selected = searchMode
)
), style = style,
showBackground = true
)
var subtitleText =
LocalContext.current.resources.getString(R.string.app_bar_subtitle)
ListItem.Item(
text = subtitleText,
subText = if (subtitle.isNullOrBlank())
LocalContext.current.resources.getString(R.string.fluentui_disabled)
else
LocalContext.current.resources.getString(R.string.fluentui_enabled),
trailingAccessoryView = {
ToggleSwitch(
onValueChange = {
subtitle =
if (subtitle.isNullOrBlank())
subtitleText
else
null
},
checkedState = !subtitle.isNullOrBlank()
)
}
)
ListItem.Item(
text = LocalContext.current.resources.getString(R.string.app_bar_style),
subText = if (style == FluentStyle.Neutral)
LocalContext.current.resources.getString(R.string.fluentui_neutral)
else
LocalContext.current.resources.getString(R.string.fluentui_brand),
trailingAccessoryView = {
ToggleSwitch(
onValueChange = {
style =
if (style == FluentStyle.Neutral)
FluentStyle.Brand
else
FluentStyle.Neutral
},
checkedState = style == FluentStyle.Brand
)
}
)
ListItem.Item(
text = LocalContext.current.resources.getString(R.string.buttonbar),
subText = if (enableButtonBar)
LocalContext.current.resources.getString(R.string.fluentui_enabled)
else
LocalContext.current.resources.getString(R.string.fluentui_disabled),
trailingAccessoryView = {
ToggleSwitch(
onValueChange = {
enableButtonBar = !enableButtonBar
},
checkedState = enableButtonBar
)
}
)
ListItem.Item(
text = LocalContext.current.resources.getString(R.string.searchbar),
subText = if (enableSearchBar)
LocalContext.current.resources.getString(R.string.fluentui_enabled)
else
LocalContext.current.resources.getString(R.string.fluentui_disabled),
trailingAccessoryView = {
enableSearchBar = enableSearchBar || searchMode
ToggleSwitch(
onValueChange = {
enableSearchBar = !enableSearchBar
},
checkedState = enableSearchBar,
enabledSwitch = !searchMode
)
}
)
}
}
val buttonBarList = mutableListOf<PillMetaData>()
for (idx in 1..6) {
buttonBarList.add(
PillMetaData(
"Button $idx",
{
Toast.makeText(
context,
"Button $idx pressed",
Toast.LENGTH_SHORT
).show()
}
)
)
}
val appTitleDelta: Float by animateFloatAsState(
if (searchMode) 0F else 1F,
animationSpec = tween(durationMillis = 150, easing = LinearEasing)
)
val yAxisDeltaCoerced = yAxisDelta.coerceIn(0F, 1F)
val accessoryDelta: Float by animateFloatAsState(yAxisDeltaCoerced)
val rightIconColor: Color = if (style == FluentStyle.Neutral)
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
FluentTheme.themeMode
)
else
FluentColor(
light = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
ThemeMode.Dark
)
).value(FluentTheme.themeMode)
AppBar(
title = "Fluent UI Demo",
navigationIcon = FluentIcon(
SearchBarIcons.Arrowback,
contentDescription = "Navigate Back",
onClick = {
Toast.makeText(
context,
"Navigation Icon pressed",
Toast.LENGTH_SHORT
).show()
}
),
subTitle = subtitle,
logo = {
Avatar(
Person(
"Allan",
"Munger",
status = AvatarStatus.DND,
isActive = true
),
enablePresence = true,
size = AvatarSize.Size32
)
},
postTitleIcon = FluentIcon(
ListItemIcons.Chevron,
contentDescription = LocalContext.current.resources.getString(R.string.fluentui_chevron),
onClick = {
Toast.makeText(context, "Title Icon pressed", Toast.LENGTH_SHORT)
.show()
}
),
postSubtitleIcon = FluentIcon(
ListItemIcons.Chevron,
contentDescription = LocalContext.current.resources.getString(R.string.fluentui_chevron),
onClick = {
Toast.makeText(context, "Subtitle Icon pressed", Toast.LENGTH_SHORT)
.show()
}
),
appBarSize = appBarSize,
style = style,
searchMode = searchMode,
searchBar = if (enableSearchBar) {
{
SearchBar(
onValueChange = { _, _ -> },
modifier = Modifier.onFocusChanged { focusState ->
when {
focusState.isFocused -> {
searchMode = true
}
}
},
style = style,
navigationIconCallback = { searchMode = false }
)
}
} else null,
bottomBar = if (enableButtonBar) {
{ PillBar(metadataList = buttonBarList, style = style) }
} else null,
appTitleDelta = appTitleDelta,
accessoryDelta = accessoryDelta,
rightAccessoryView = {
Box(
Modifier
.size(44.dp)
.clickable(
onClick = {
Toast
.makeText(
context,
"Navigation Icon 1 Pressed",
Toast.LENGTH_SHORT
)
.show()
}
),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.Add,
"Add",
tint = rightIconColor
)
}
Box(
Modifier
.size(44.dp)
.clickable(
onClick = {
Toast
.makeText(
context,
"Navigation Icon 2 Pressed",
Toast.LENGTH_SHORT
)
.show()
}
),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.Email,
"E-mail",
tint = rightIconColor
)
}
}
)
}
}
}
}
}

Просмотреть файл

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
-->
<resources xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<!--App-->
<string name="app_name">Fluent UI Demo</string>
@ -35,8 +33,12 @@
<string name="actionbar_position_heading">ActionBar Position</string>
<!--AppBar-->
<string name="app_bar_style">AppBar Style</string>
<string name="app_bar_subtitle">Subtitle</string>
<!-- Text that shows the action performed to the user -->
<string name="app_bar_layout_navigation_icon_clicked">Navigation icon clicked.</string>
<!-- Icon labels -->
<string name="app_bar_layout_menu_flag">Flag</string>
<string name="app_bar_layout_menu_settings">Settings</string>
@ -127,7 +129,7 @@
<string name="bottom_sheet_dialog_button">Show</string>
<string name="drawer_content_desc_collapse_state">Expand</string>
<string name="drawer_content_desc_expand_state">Minimize</string>
<string name="large_scrollable_text" translatable="false" >Large demo Text line for showing scroll behaviour of a content. Use inside a Scrollview \n
<string name="large_scrollable_text" translatable="false">Large demo Text line for showing scroll behaviour of a content. Use inside a Scrollview \n
Large demo Text line for showing scroll behaviour of a content. Use inside a Scrollview \n
Large demo Text line for showing scroll behaviour of a content. Use inside a Scrollview \n
Large demo Text line for showing scroll behaviour of a content. Use inside a Scrollview \n
@ -296,6 +298,7 @@
<!--Button-->
<!-- text for buttons -->
<string name="button">Button</string>
<string name="buttonbar">ButtonBar</string>
<string name="button_disabled">Disabled Button</string>
<string name="button_borderless">Borderless Button</string>
<string name="button_borderless_disabled">Borderless Disabled Button</string>
@ -587,6 +590,7 @@
Ut enim ad minim veniam.</string>
<!--SearchBar-->
<string name="searchbar">SearchBar</string>
<!-- name of the icon-->
<string name="searchbar_microphone_callback">Microphone Callback</string>
<!-- UI Text label -->

Просмотреть файл

@ -15,6 +15,7 @@ interface ControlToken
class ControlTokens {
enum class ControlType {
AppBar,
Avatar,
AvatarCarousel,
AvatarGroup,
@ -44,6 +45,7 @@ class ControlTokens {
val tokens: TokenSet<ControlType, ControlToken> by lazy {
TokenSet { token ->
when (token) {
ControlType.AppBar -> AppBarTokens()
ControlType.Avatar -> AvatarTokens()
ControlType.AvatarCarousel -> AvatarCarouselTokens()
ControlType.AvatarGroup -> AvatarGroupTokens()

Просмотреть файл

@ -2,14 +2,15 @@ package com.microsoft.fluentui.theme.token
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.theme.ThemeMode
data class Icon(
data class FluentIcon(
val light: ImageVector = ImageVector.Builder("", 0.dp, 0.dp, 0F, 0F).build(),
val dark: ImageVector = ImageVector.Builder("", 0.dp, 0.dp, 0F, 0F).build(),
val dark: ImageVector = light,
val contentDescription: String = "",
val onClick: (() -> Unit)? = null
) {
@Composable
fun value(themeMode: ThemeMode = com.microsoft.fluentui.theme.FluentTheme.themeMode): ImageVector {
@ -17,7 +18,11 @@ data class Icon(
ThemeMode.Light -> light
ThemeMode.Dark -> dark
ThemeMode.Auto -> if (isSystemInDarkTheme()) dark else light
else -> ImageVector.Builder("", 0.dp, 0.dp, 0F, 0F).build()
}
}
fun isIconAvailable(): Boolean {
return (this.light.defaultWidth > 0.dp && this.light.defaultHeight > 0.dp) ||
(this.dark.defaultWidth > 0.dp && this.dark.defaultHeight > 0.dp)
}
}

Просмотреть файл

@ -0,0 +1,203 @@
package com.microsoft.fluentui.theme.token.controlTokens
import android.os.Parcelable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.ThemeMode
import com.microsoft.fluentui.theme.token.*
import kotlinx.parcelize.Parcelize
enum class AppBarSize {
Large,
Medium,
Small
}
data class AppBarInfo(
val style: FluentStyle = FluentStyle.Neutral,
val appBarSize: AppBarSize = AppBarSize.Medium
) : ControlInfo
@Parcelize
open class AppBarTokens : ControlToken, Parcelable {
@Composable
open fun backgroundColor(info: AppBarInfo): Color {
return when (info.style) {
FluentStyle.Neutral ->
FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background3].value(
themeMode = FluentTheme.themeMode
)
FluentStyle.Brand ->
FluentColor(
light = FluentTheme.aliasTokens.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background3].value(
ThemeMode.Dark
)
).value(themeMode = FluentTheme.themeMode)
}
}
@Composable
open fun navigationIconColor(info: AppBarInfo): Color {
return when (info.style) {
FluentStyle.Neutral ->
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
themeMode = FluentTheme.themeMode
)
FluentStyle.Brand ->
FluentColor(
light = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
ThemeMode.Dark
)
).value(themeMode = FluentTheme.themeMode)
}
}
@Composable
open fun titleIconColor(info: AppBarInfo): Color {
return when (info.style) {
FluentStyle.Neutral ->
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
themeMode = FluentTheme.themeMode
)
FluentStyle.Brand ->
FluentColor(
light = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
ThemeMode.Dark
)
).value(themeMode = FluentTheme.themeMode)
}
}
@Composable
open fun subtitleIconColor(info: AppBarInfo): Color {
return when (info.style) {
FluentStyle.Neutral ->
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
themeMode = FluentTheme.themeMode
)
FluentStyle.Brand ->
FluentColor(
light = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
ThemeMode.Dark
)
).value(themeMode = FluentTheme.themeMode)
}
}
@Composable
open fun titleTextColor(info: AppBarInfo): Color {
return when (info.style) {
FluentStyle.Neutral ->
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
themeMode = FluentTheme.themeMode
)
FluentStyle.Brand ->
FluentColor(
light = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
ThemeMode.Dark
)
).value(themeMode = FluentTheme.themeMode)
}
}
@Composable
open fun subtitleTextColor(info: AppBarInfo): Color {
return when (info.style) {
FluentStyle.Neutral ->
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
themeMode = FluentTheme.themeMode
)
FluentStyle.Brand ->
FluentColor(
light = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value(
ThemeMode.Light
),
dark = FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground2].value(
ThemeMode.Dark
)
).value(themeMode = FluentTheme.themeMode)
}
}
@Composable
open fun titleTypography(info: AppBarInfo): FontInfo {
return when (info.appBarSize) {
AppBarSize.Large -> FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Title1]
AppBarSize.Medium -> FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Title2]
AppBarSize.Small -> FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Body1Strong]
else -> FontInfo(fontSize = FontSize(0.sp, 0.sp))
}
}
@Composable
open fun subtitleTypography(info: AppBarInfo): FontInfo {
return FluentTheme.aliasTokens.typography[AliasTokens.TypographyTokens.Caption1]
}
@Composable
fun leftIconSize(info: AppBarInfo): Dp {
return GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.Medium)
}
@Composable
fun titleIconSize(info: AppBarInfo): Dp {
return when (info.appBarSize) {
AppBarSize.Small -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XSmall)
else -> 0.dp
}
}
@Composable
fun subtitleIconSize(info: AppBarInfo): Dp {
return when (info.appBarSize) {
AppBarSize.Small -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XXSmall)
AppBarSize.Medium -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XXSmall)
else -> 0.dp
}
}
@Composable
open fun navigationIconPadding(info: AppBarInfo): PaddingValues {
return when (info.appBarSize) {
AppBarSize.Large -> PaddingValues()
AppBarSize.Medium -> PaddingValues(16.dp)
AppBarSize.Small -> PaddingValues(16.dp)
}
}
@Composable
open fun textPadding(info: AppBarInfo): PaddingValues {
return when (info.appBarSize) {
AppBarSize.Large -> PaddingValues(start = 12.dp)
AppBarSize.Medium -> PaddingValues()
AppBarSize.Small -> PaddingValues(start = 8.dp)
}
}
@Composable
open fun height(info: AppBarInfo): Dp {
return 40.dp
}
}

Просмотреть файл

@ -351,36 +351,36 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
}
@Composable
open fun presenceIcon(avatarInfo: AvatarInfo): Icon {
open fun presenceIcon(avatarInfo: AvatarInfo): FluentIcon {
return when (avatarInfo.status) {
AvatarStatus.Available ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Small.Light else AvatarIcons.Presence.Available.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Small.Dark else AvatarIcons.Presence.Available.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Small.Light else AvatarIcons.Presence.Available.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Small.Dark else AvatarIcons.Presence.Available.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Small.Light else AvatarIcons.Presence.Available.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Small.Dark else AvatarIcons.Presence.Available.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Medium.Light else AvatarIcons.Presence.Available.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Medium.Dark else AvatarIcons.Presence.Available.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Medium.Light else AvatarIcons.Presence.Available.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Medium.Dark else AvatarIcons.Presence.Available.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Large.Light else AvatarIcons.Presence.Available.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Availableoof.Large.Dark else AvatarIcons.Presence.Available.Large.Dark
)
@ -388,33 +388,33 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
AvatarStatus.Busy ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Small.Light else AvatarIcons.Presence.Busy.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Small.Dark else AvatarIcons.Presence.Busy.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Small.Light else AvatarIcons.Presence.Busy.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Small.Dark else AvatarIcons.Presence.Busy.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Small.Light else AvatarIcons.Presence.Busy.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Small.Dark else AvatarIcons.Presence.Busy.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Medium.Light else AvatarIcons.Presence.Busy.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Medium.Dark else AvatarIcons.Presence.Busy.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Medium.Light else AvatarIcons.Presence.Busy.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Medium.Dark else AvatarIcons.Presence.Busy.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Large.Light else AvatarIcons.Presence.Busy.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Busyoof.Large.Dark else AvatarIcons.Presence.Busy.Large.Dark
)
@ -422,33 +422,33 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
AvatarStatus.Away ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Small.Light else AvatarIcons.Presence.Away.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Small.Dark else AvatarIcons.Presence.Away.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Small.Light else AvatarIcons.Presence.Away.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Small.Dark else AvatarIcons.Presence.Away.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Small.Light else AvatarIcons.Presence.Away.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Small.Dark else AvatarIcons.Presence.Away.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Medium.Light else AvatarIcons.Presence.Away.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Medium.Dark else AvatarIcons.Presence.Away.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Medium.Light else AvatarIcons.Presence.Away.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Medium.Dark else AvatarIcons.Presence.Away.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Large.Light else AvatarIcons.Presence.Away.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Awayoof.Large.Dark else AvatarIcons.Presence.Away.Large.Dark
)
@ -456,33 +456,33 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
AvatarStatus.DND ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Small.Light else AvatarIcons.Presence.Dnd.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Small.Dark else AvatarIcons.Presence.Dnd.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Small.Light else AvatarIcons.Presence.Dnd.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Small.Dark else AvatarIcons.Presence.Dnd.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Small.Light else AvatarIcons.Presence.Dnd.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Small.Dark else AvatarIcons.Presence.Dnd.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Medium.Light else AvatarIcons.Presence.Dnd.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Medium.Dark else AvatarIcons.Presence.Dnd.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Medium.Light else AvatarIcons.Presence.Dnd.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Medium.Dark else AvatarIcons.Presence.Dnd.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Large.Light else AvatarIcons.Presence.Dnd.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Dndoof.Large.Dark else AvatarIcons.Presence.Dnd.Large.Dark
)
@ -490,33 +490,33 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
AvatarStatus.Unknown ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Unknown.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Unknown.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Unknown.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Unknown.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Unknown.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Unknown.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Light else AvatarIcons.Presence.Unknown.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Dark else AvatarIcons.Presence.Unknown.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Light else AvatarIcons.Presence.Unknown.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Dark else AvatarIcons.Presence.Unknown.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Large.Light else AvatarIcons.Presence.Unknown.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Large.Dark else AvatarIcons.Presence.Unknown.Large.Dark
)
@ -524,33 +524,33 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
AvatarStatus.Blocked ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Blocked.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Blocked.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Blocked.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Blocked.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Blocked.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Blocked.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Light else AvatarIcons.Presence.Blocked.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Dark else AvatarIcons.Presence.Blocked.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Light else AvatarIcons.Presence.Blocked.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Dark else AvatarIcons.Presence.Blocked.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Large.Light else AvatarIcons.Presence.Blocked.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Large.Dark else AvatarIcons.Presence.Blocked.Large.Dark
)
@ -558,33 +558,33 @@ open class AvatarTokens(private val activityRingToken: ActivityRingsToken = Acti
AvatarStatus.Offline ->
when (avatarInfo.size) {
AvatarSize.Size16 -> Icon()
AvatarSize.Size20 -> Icon(
AvatarSize.Size16 -> FluentIcon()
AvatarSize.Size20 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Offline.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Offline.Small.Dark
)
AvatarSize.Size24 -> Icon(
AvatarSize.Size24 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Offline.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Offline.Small.Dark
)
AvatarSize.Size32 -> Icon(
AvatarSize.Size32 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Light else AvatarIcons.Presence.Offline.Small.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Small.Dark else AvatarIcons.Presence.Offline.Small.Dark
)
AvatarSize.Size40 -> Icon(
AvatarSize.Size40 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Light else AvatarIcons.Presence.Offline.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Dark else AvatarIcons.Presence.Offline.Medium.Dark
)
AvatarSize.Size56 -> Icon(
AvatarSize.Size56 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Light else AvatarIcons.Presence.Offline.Medium.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Medium.Dark else AvatarIcons.Presence.Offline.Medium.Dark
)
AvatarSize.Size72 -> Icon(
AvatarSize.Size72 -> FluentIcon(
light = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Large.Light else AvatarIcons.Presence.Offline.Large.Light,
dark = if (avatarInfo.isOOO) AvatarIcons.Presence.Oof.Large.Dark else AvatarIcons.Presence.Offline.Large.Dark
)

Просмотреть файл

@ -134,8 +134,8 @@ open class SearchBarTokens : ControlToken, Parcelable {
}
@Composable
open fun padding(searchBarInfo: SearchBarInfo): PaddingValues {
return PaddingValues(GlobalTokens.size(GlobalTokens.SizeTokens.Size80))
open fun searchBarPadding(info: SearchBarInfo): PaddingValues {
return PaddingValues(horizontal = GlobalTokens.size(GlobalTokens.SizeTokens.Size80))
}
@Composable

Просмотреть файл

@ -30,4 +30,8 @@
<string name="fluentui_brand">Brand</string>
<!--name of the icon -->
<string name="fluentui_chevron">Chevron</string>
<string name="fluentui_large">Large</string>
<string name="fluentui_medium">Medium</string>
<string name="fluentui_small">Small</string>
<string name="fluentui_subtitle">Subtitle</string>
</resources>

Просмотреть файл

@ -27,7 +27,7 @@ import androidx.compose.ui.unit.DpOffset
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.FluentTheme.themeMode
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.Icon
import com.microsoft.fluentui.theme.token.FluentIcon
import com.microsoft.fluentui.theme.token.controlTokens.AvatarInfo
import com.microsoft.fluentui.theme.token.controlTokens.AvatarSize
import com.microsoft.fluentui.theme.token.controlTokens.AvatarTokens
@ -145,7 +145,7 @@ fun Avatar(
if (enablePresence) {
val presenceOffset: DpOffset = getAvatarTokens().presenceOffset(getAvatarInfo())
val image: Icon = getAvatarTokens().presenceIcon(getAvatarInfo())
val image: FluentIcon = getAvatarTokens().presenceIcon(getAvatarInfo())
Image(
image.value(themeMode),
null,

Просмотреть файл

@ -0,0 +1,299 @@
package com.microsoft.fluentui.tokenized
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.scale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.core.R
import com.microsoft.fluentui.icons.ListItemIcons
import com.microsoft.fluentui.icons.SearchBarIcons
import com.microsoft.fluentui.icons.listitemicons.Chevron
import com.microsoft.fluentui.icons.searchbaricons.Arrowback
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.FluentTheme.themeMode
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.FluentIcon
import com.microsoft.fluentui.theme.token.FluentStyle
import com.microsoft.fluentui.theme.token.controlTokens.AppBarInfo
import com.microsoft.fluentui.theme.token.controlTokens.AppBarSize
import com.microsoft.fluentui.theme.token.controlTokens.AppBarTokens
private val LocalAppBarTokens = compositionLocalOf { AppBarTokens() }
private val LocalAppBarInfo = compositionLocalOf { AppBarInfo(FluentStyle.Neutral) }
/**
* An app bar appears at the top of an app screen, below the status bar,
* and enables navigation through a series of hierarchical screens.
* When a new screen is displayed, a back button, often labeled with the title of
* the previous screen, appears on the left side of the bar. Sometimes, the right side
* of a navigation bar contains a control, like an Edit or a Done button,
* for managing the content within the active view. In a split view,
* a navigation bar may appear in a single pane of the split view.
* Navigation bars are translucent, may have a background tint, and can be configured
* to hide when the keyboard is onscreen, a gesture occurs, or a view resizes.
*
* @param title Title Of the current page
* @param modifier Optional Modifier for updating appbar
* @param appBarSize Enum to define App Bar Size. Default: [AppBarSize.Medium]
* @param style Fluent Style of AppBar. Default: [FluentStyle.Neutral]
* @param subTitle Subtitle to be displayed. Default: [null]
* @param logo Composable to be placed at left of Title. Guideline is to not increase a size of 32x32. Default: [null]
* @param searchMode Boolean to enable/disable searchMode. Default: [false]
* @param navigationIcon Navigate Back Icon to be placed at extreme left. Default: [SearchBarIcons.Arrowback]
* @param postTitleIcon Icon to be placed after title making the title clickable. Default: Empty [FluentIcon]
* @param preSubtitleIcon Icon to be placed before subtitle. Default: Empty [FluentIcon]
* @param postSubtitleIcon Icon to be placed after subtitle. Default: [ListItemIcons.Chevron]
* @param rightAccessoryView Row Placeholder to be placed at right on AppTitle. Default: [null]
* @param searchBar Composable to be placed as searchbar below appTitle. Default: [null]
* @param bottomBar Composable to Be placed below appTitle. Displayed if searchbar is not provided or when in searchmode. Default: [null]
* @param appTitleDelta Ratio of opening of appTitle. Used for Shychrome and other animations. Default: [1.0F]
* @param accessoryDelta Ratio of opening of accessory View. Used for Shychrome and other animations. Default: [1.0F]
* @param appBarTokens Optional Tokens for App Bar to customize it. Default: [null]
*/
@OptIn(ExperimentalTextApi::class)
@Composable
fun AppBar(
title: String,
modifier: Modifier = Modifier,
appBarSize: AppBarSize = AppBarSize.Medium,
style: FluentStyle = FluentStyle.Neutral,
subTitle: String? = null,
logo: @Composable (() -> Unit)? = null,
searchMode: Boolean = false,
navigationIcon: FluentIcon = FluentIcon(SearchBarIcons.Arrowback),
postTitleIcon: FluentIcon = FluentIcon(),
preSubtitleIcon: FluentIcon = FluentIcon(),
postSubtitleIcon: FluentIcon = FluentIcon(
ListItemIcons.Chevron,
contentDescription = LocalContext.current.resources.getString(R.string.fluentui_chevron)
),
rightAccessoryView: @Composable (RowScope.() -> Unit)? = null,
searchBar: @Composable (RowScope.() -> Unit)? = null,
bottomBar: @Composable (RowScope.() -> Unit)? = null,
appTitleDelta: Float = 1.0F,
accessoryDelta: Float = 1.0F,
appBarTokens: AppBarTokens? = null
) {
val token = appBarTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.AppBar] as AppBarTokens
CompositionLocalProvider(
LocalAppBarTokens provides token,
LocalAppBarInfo provides AppBarInfo(style, appBarSize)
) {
Surface(
modifier = modifier
.fillMaxWidth()
) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(getAppBarTokens().backgroundColor(getAppBarInfo()))
) {
Row(
Modifier
.requiredHeight(56.dp * appTitleDelta)
.animateContentSize()
.fillMaxWidth()
.padding(horizontal = 16.dp)
.scale(scaleX = 1.0F, scaleY = appTitleDelta)
.alpha(if (appTitleDelta != 1.0F) appTitleDelta / 3 else 1.0F),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
if (appBarSize != AppBarSize.Large && navigationIcon.isIconAvailable()) {
Box(
modifier = Modifier
.then(
if (navigationIcon.onClick != null) {
Modifier.clickable(
role = Role.Button,
onClick = navigationIcon.onClick!!
)
} else
Modifier
), contentAlignment = Alignment.Center
) {
Icon(
navigationIcon.value(),
navigationIcon.contentDescription,
modifier = Modifier
.padding(getAppBarTokens().navigationIconPadding(getAppBarInfo()))
.size(getAppBarTokens().leftIconSize(getAppBarInfo())),
tint = getAppBarTokens().navigationIconColor(getAppBarInfo())
)
}
}
if (appBarSize != AppBarSize.Medium)
logo?.invoke()
val titleFontInfo = getAppBarTokens().titleTypography(getAppBarInfo())
val subtitleFontInfo =
getAppBarTokens().subtitleTypography(getAppBarInfo())
if (appBarSize != AppBarSize.Large && !subTitle.isNullOrBlank()) {
Column(
modifier = Modifier
.weight(1F)
.padding(getAppBarTokens().textPadding(getAppBarInfo()))
) {
Row(
modifier = Modifier
.then(
if (postTitleIcon.onClick != null && appBarSize == AppBarSize.Small)
Modifier.clickable(onClick = postTitleIcon.onClick!!)
else
Modifier
), verticalAlignment = Alignment.CenterVertically
) {
Text(
text = title,
style = TextStyle(
color = getAppBarTokens().titleTextColor(getAppBarInfo()),
fontSize = titleFontInfo.fontSize.size,
lineHeight = titleFontInfo.fontSize.lineHeight,
fontWeight = titleFontInfo.weight,
platformStyle = PlatformTextStyle(
includeFontPadding = false
)
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (postTitleIcon.isIconAvailable() && appBarSize == AppBarSize.Small)
Icon(
postTitleIcon.value(themeMode),
postTitleIcon.contentDescription,
modifier = Modifier
.size(getAppBarTokens().titleIconSize(getAppBarInfo())),
tint = getAppBarTokens().titleIconColor(getAppBarInfo())
)
}
Row(
modifier = Modifier
.then(
if (postSubtitleIcon.onClick != null)
Modifier.clickable(onClick = postSubtitleIcon.onClick!!)
else
Modifier
), verticalAlignment = Alignment.CenterVertically
) {
if (preSubtitleIcon.isIconAvailable())
Icon(
preSubtitleIcon.value(themeMode),
preSubtitleIcon.contentDescription,
modifier = Modifier
.size(
getAppBarTokens().subtitleIconSize(
getAppBarInfo()
)
),
tint = getAppBarTokens().subtitleIconColor(getAppBarInfo())
)
Text(
subTitle,
style = TextStyle(
color = getAppBarTokens().subtitleTextColor(
getAppBarInfo()
),
fontSize = subtitleFontInfo.fontSize.size,
lineHeight = subtitleFontInfo.fontSize.lineHeight,
fontWeight = subtitleFontInfo.weight
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (postSubtitleIcon.isIconAvailable())
Icon(
postSubtitleIcon.value(themeMode),
postSubtitleIcon.contentDescription,
modifier = Modifier
.size(
getAppBarTokens().subtitleIconSize(
getAppBarInfo()
)
),
tint = getAppBarTokens().subtitleIconColor(getAppBarInfo())
)
}
}
} else {
Text(
text = title,
modifier = Modifier
.padding(getAppBarTokens().textPadding(getAppBarInfo()))
.weight(1F),
style = TextStyle(
color = getAppBarTokens().titleTextColor(getAppBarInfo()),
fontSize = titleFontInfo.fontSize.size,
lineHeight = titleFontInfo.fontSize.lineHeight,
fontWeight = titleFontInfo.weight,
platformStyle = PlatformTextStyle(
includeFontPadding = false
)
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
if (rightAccessoryView != null) {
rightAccessoryView()
}
}
if (searchBar != null) {
Row(
modifier
.animateContentSize()
.fillMaxWidth()
.then(if (!searchMode) Modifier.height(56.dp * accessoryDelta) else Modifier)
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.Center
) {
searchBar()
}
}
if (bottomBar != null && (searchMode || searchBar == null)) {
Row(
Modifier
.animateContentSize()
.fillMaxWidth()
.then(if (!searchMode) Modifier.height(48.dp * accessoryDelta) else Modifier)
.padding(vertical = 8.dp)
) {
bottomBar()
}
}
}
}
}
}
@Composable
private fun getAppBarTokens(): AppBarTokens {
return LocalAppBarTokens.current
}
@Composable
private fun getAppBarInfo(): AppBarInfo {
return LocalAppBarInfo.current
}

Просмотреть файл

@ -64,9 +64,11 @@ fun SearchBar(
keyboardOptions: KeyboardOptions = KeyboardOptions(),
keyboardActions: KeyboardActions = KeyboardActions(),
searchHint: String = LocalContext.current.resources.getString(R.string.fluentui_search),
focusByDefault: Boolean = false,
selectedPerson: Person? = null,
personaChipOnClick: (() -> Unit)? = null,
microphoneCallback: (() -> Unit)? = null,
navigationIconCallback: (() -> Unit)? = null,
rightAccessoryIcon: ImageVector? = null,
rightAccessoryIconDescription: String = "",
rightAccessoryViewOnClick: (() -> Unit)? = null,
@ -93,279 +95,296 @@ fun SearchBar(
val scope = rememberCoroutineScope()
Row(
modifier
.requiredHeight(getSearchBarTokens().height(getSearchBarInfo()))
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.background(
getSearchBarTokens().inputBackgroundColor(getSearchBarInfo()),
RoundedCornerShape(8.dp)
),
verticalAlignment = Alignment.CenterVertically
modifier = modifier
.background(getSearchBarTokens().backgroundColor(getSearchBarInfo()))
.padding(getSearchBarTokens().searchBarPadding(getSearchBarInfo()))
) {
//Left Section
AnimatedContent(searchHasFocus) {
var onClick: (() -> Unit)? = null
var icon: ImageVector? = null
var contentDescription: String? = null
Row(
Modifier
.requiredHeight(getSearchBarTokens().height(getSearchBarInfo()))
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.background(
getSearchBarTokens().inputBackgroundColor(getSearchBarInfo()),
RoundedCornerShape(8.dp)
),
verticalAlignment = Alignment.CenterVertically
) {
//Left Section
AnimatedContent(searchHasFocus) {
var onClick: (() -> Unit)? = null
var icon: ImageVector? = null
var contentDescription: String? = null
when (it) {
true -> {
onClick = {
scope.launch {
queryText = ""
when (it) {
true -> {
onClick = {
scope.launch {
queryText = ""
selectedPerson = null
searching = true
onValueChange(queryText, selectedPerson)
searching = false
focusManager.clearFocus()
searchHasFocus = false
}
navigationIconCallback?.invoke()
}
icon = SearchBarIcons.Arrowback
contentDescription =
LocalContext.current.resources.getString(R.string.fluentui_back)
}
false -> {
onClick = {
scope.launch {
focusRequester.requestFocus()
}
}
icon = SearchBarIcons.Search
contentDescription =
LocalContext.current.resources.getString(R.string.fluentui_search)
}
}
Box(
modifier = Modifier
.size(44.dp, 40.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = onClick,
role = Role.Button
),
contentAlignment = Alignment.Center
) {
Icon(
icon,
contentDescription,
modifier = Modifier
.size(getSearchBarTokens().leftIconSize(getSearchBarInfo())),
tint = getSearchBarTokens().leftIconColor(getSearchBarInfo())
)
}
}
//Center Section
Row(
modifier = Modifier
.height(24.dp)
.weight(1F)
.onKeyEvent {
if (it.key == Key.Backspace) {
scope.launch {
if (personaChipSelected) {
selectedPerson = null
personaChipSelected = false
searching = true
onValueChange(queryText, selectedPerson)
searching = false
} else {
personaChipSelected = true
}
}
}
true
},
verticalAlignment = Alignment.CenterVertically
) {
LaunchedEffect(selectedPerson) {
queryText = ""
if (personaChipSelected)
personaChipSelected = false
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
if (selectedPerson != null) {
SearchBarPersonaChip(
person = selectedPerson!!,
modifier = Modifier.padding(end = 8.dp),
style = style,
enabled = enabled,
selected = personaChipSelected,
onClick = {
personaChipSelected = !personaChipSelected
personaChipOnClick?.invoke()
},
onCloseClick = {
selectedPerson = null
searching = true
onValueChange(queryText, selectedPerson)
searching = false
focusManager.clearFocus()
searchHasFocus = false
}
}
icon = SearchBarIcons.Arrowback
contentDescription =
LocalContext.current.resources.getString(R.string.fluentui_back)
)
}
false -> {
onClick = {
BasicTextField(
value = queryText,
onValueChange = {
scope.launch {
focusRequester.requestFocus()
queryText = it
personaChipSelected = false
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
},
singleLine = true,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
modifier = Modifier
.fillMaxHeight()
.weight(1F)
.focusRequester(focusRequester)
.onFocusChanged { focusState ->
when {
focusState.isFocused ->
searchHasFocus = true
}
},
textStyle = TextStyle(
fontSize = getSearchBarTokens().typography(getSearchBarInfo()).fontSize.size,
lineHeight = getSearchBarTokens().typography(getSearchBarInfo()).fontSize.size,
color = getSearchBarTokens().textColor(getSearchBarInfo())
),
decorationBox = @Composable { innerTextField ->
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.CenterStart
) {
if (queryText.isEmpty()) {
Text(
searchHint,
style = TextStyle(
fontSize = getSearchBarTokens().typography(
getSearchBarInfo()
).fontSize.size,
lineHeight = getSearchBarTokens().typography(
getSearchBarInfo()
).fontSize.size,
color = getSearchBarTokens().textColor(getSearchBarInfo()),
)
)
}
innerTextField()
}
}
icon = SearchBarIcons.Search
contentDescription =
LocalContext.current.resources.getString(R.string.fluentui_search)
}
}
Box(
modifier = Modifier
.size(44.dp, 40.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = onClick,
role = Role.Button
),
contentAlignment = Alignment.Center
) {
Icon(
icon,
contentDescription,
modifier = Modifier
.size(getSearchBarTokens().leftIconSize(getSearchBarInfo())),
tint = getSearchBarTokens().leftIconColor(getSearchBarInfo())
)
}
}
LaunchedEffect(Unit) {
if (focusByDefault)
focusRequester.requestFocus()
}
//Center Section
Row(
modifier = Modifier
.height(24.dp)
.weight(1F)
.onKeyEvent {
if (it.key == Key.Backspace) {
scope.launch {
if (personaChipSelected) {
selectedPerson = null
personaChipSelected = false
searching = true
onValueChange(queryText, selectedPerson)
searching = false
} else {
personaChipSelected = true
//Right Section
AnimatedContent((queryText.isBlank() && selectedPerson == null)) {
when (it) {
true ->
if (microphoneCallback != null) {
Box(
modifier = Modifier
.size(44.dp, 40.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = microphoneCallback,
role = Role.Button
),
contentAlignment = Alignment.Center
) {
Icon(
SearchBarIcons.Microphone,
LocalContext.current.resources.getString(R.string.fluentui_microphone),
modifier = Modifier
.size(
getSearchBarTokens().rightIconSize(
getSearchBarInfo()
)
),
tint = getSearchBarTokens().rightIconColor(getSearchBarInfo())
)
}
}
}
true
},
verticalAlignment = Alignment.CenterVertically
) {
LaunchedEffect(selectedPerson) {
queryText = ""
if (personaChipSelected)
personaChipSelected = false
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
if (selectedPerson != null) {
SearchBarPersonaChip(
person = selectedPerson!!,
modifier = Modifier.padding(end = 8.dp),
style = style,
enabled = enabled,
selected = personaChipSelected,
onClick = {
personaChipSelected = !personaChipSelected
personaChipOnClick?.invoke()
},
onCloseClick = {
selectedPerson = null
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
)
}
BasicTextField(
value = queryText,
onValueChange = {
scope.launch {
queryText = it
personaChipSelected = false
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
},
singleLine = true,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
modifier = Modifier
.fillMaxHeight()
.weight(1F)
.focusRequester(focusRequester)
.onFocusChanged { focusState ->
when {
focusState.isFocused ->
searchHasFocus = true
}
},
textStyle = TextStyle(
fontSize = getSearchBarTokens().typography(getSearchBarInfo()).fontSize.size,
lineHeight = getSearchBarTokens().typography(getSearchBarInfo()).fontSize.size,
color = getSearchBarTokens().textColor(getSearchBarInfo())
),
decorationBox = @Composable { innerTextField ->
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.CenterStart
) {
if (queryText.isEmpty()) {
Text(
searchHint,
style = TextStyle(
fontSize = getSearchBarTokens().typography(getSearchBarInfo()).fontSize.size,
lineHeight = getSearchBarTokens().typography(
getSearchBarInfo()
).fontSize.size,
color = getSearchBarTokens().textColor(getSearchBarInfo()),
)
)
}
innerTextField()
}
}
)
}
//Right Section
AnimatedContent((queryText.isBlank() && selectedPerson == null)) {
when (it) {
true ->
if (microphoneCallback != null) {
false ->
Box(
modifier = Modifier
.size(44.dp, 40.dp)
.padding(
getSearchBarTokens().progressIndicatorRightPadding(
getSearchBarInfo()
)
)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = microphoneCallback,
onClick = {
scope.launch {
queryText = ""
selectedPerson = null
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
},
role = Role.Button
),
contentAlignment = Alignment.Center
) {
if (searching)
CircularProgressIndicator(
size = getSearchBarTokens().circularProgressIndicatorSize(
getSearchBarInfo()
)
)
Icon(
SearchBarIcons.Microphone,
LocalContext.current.resources.getString(R.string.fluentui_microphone),
SearchBarIcons.Dismisscircle,
LocalContext.current.resources.getString(R.string.fluentui_clear_text),
modifier = Modifier
.size(getSearchBarTokens().rightIconSize(getSearchBarInfo())),
tint = getSearchBarTokens().rightIconColor(getSearchBarInfo())
)
}
}
false ->
Box(
modifier = Modifier
.size(44.dp, 40.dp)
.padding(
getSearchBarTokens().progressIndicatorRightPadding(
getSearchBarInfo()
)
)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = {
scope.launch {
queryText = ""
selectedPerson = null
searching = true
onValueChange(queryText, selectedPerson)
searching = false
}
},
role = Role.Button
),
contentAlignment = Alignment.Center
) {
if (searching)
CircularProgressIndicator(
size = getSearchBarTokens().circularProgressIndicatorSize(
getSearchBarInfo()
)
)
Icon(
SearchBarIcons.Dismisscircle,
LocalContext.current.resources.getString(R.string.fluentui_clear_text),
modifier = Modifier
.size(getSearchBarTokens().rightIconSize(getSearchBarInfo())),
tint = getSearchBarTokens().rightIconColor(getSearchBarInfo())
)
}
}
}
}
if (rightAccessoryIcon != null && rightAccessoryViewOnClick != null) {
Row(
modifier = Modifier
.size(44.dp, 40.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = rightAccessoryViewOnClick,
role = Role.Button
),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
rightAccessoryIcon,
rightAccessoryIconDescription,
if (rightAccessoryIcon != null && rightAccessoryViewOnClick != null) {
Row(
modifier = Modifier
.size(getSearchBarTokens().rightIconSize(getSearchBarInfo())),
tint = getSearchBarTokens().rightIconColor(getSearchBarInfo())
)
Icon(
ListItemIcons.Chevron,
LocalContext.current.resources.getString(R.string.fluentui_chevron),
Modifier.rotate(90F)
)
.size(44.dp, 40.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = enabled,
onClick = rightAccessoryViewOnClick,
role = Role.Button
),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
rightAccessoryIcon,
rightAccessoryIconDescription,
modifier = Modifier
.size(getSearchBarTokens().rightIconSize(getSearchBarInfo())),
tint = getSearchBarTokens().rightIconColor(getSearchBarInfo())
)
Icon(
ListItemIcons.Chevron,
LocalContext.current.resources.getString(R.string.fluentui_chevron),
Modifier.rotate(90F)
)
}
}
}
}