Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.ItemListingExpandableFabAction
import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.VaultDropdownMenuAction
import com.bitwarden.authenticator.ui.authenticator.feature.model.SharedCodesDisplayState
import com.bitwarden.authenticator.ui.authenticator.feature.model.VerificationCodeDisplayItem
Expand All @@ -73,7 +72,8 @@ import com.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
import com.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.bitwarden.ui.platform.components.fab.BitwardenExpandableFloatingActionButton
import com.bitwarden.ui.platform.components.fab.ExpandableFabIcon
import com.bitwarden.ui.platform.components.fab.model.ExpandableFabIcon
import com.bitwarden.ui.platform.components.fab.model.ExpandableFabOption
import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
Expand Down Expand Up @@ -188,25 +188,25 @@ fun ItemListingScreen(
BitwardenExpandableFloatingActionButton(
modifier = Modifier.testTag("AddItemButton"),
items = persistentListOf(
ItemListingExpandableFabAction.ScanQrCode(
ExpandableFabOption(
label = BitwardenString.scan_a_qr_code.asText(),
icon = IconData.Local(
iconRes = BitwardenDrawable.ic_camera_small,
contentDescription = BitwardenString.scan_a_qr_code.asText(),
testTag = "ScanQRCodeButton",
),
onScanQrCodeClick = remember(viewModel) {
onFabOptionClick = remember(viewModel) {
{ launcher.launch(Manifest.permission.CAMERA) }
},
),
ItemListingExpandableFabAction.EnterSetupKey(
ExpandableFabOption(
label = BitwardenString.enter_key_manually.asText(),
icon = IconData.Local(
iconRes = BitwardenDrawable.ic_lock_encrypted_small,
contentDescription = BitwardenString.enter_key_manually.asText(),
testTag = "EnterSetupKeyButton",
),
onEnterSetupKeyClick = remember(viewModel) {
onFabOptionClick = remember(viewModel) {
{ viewModel.trySendAction(ItemListingAction.EnterSetupKeyClick) }
},
),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.draw.rotate
import androidx.compose.ui.unit.dp
import com.bitwarden.ui.platform.base.util.nullableTestTag
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.fab.model.ExpandableFabIcon
import com.bitwarden.ui.platform.components.fab.model.ExpandableFabOption
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.bitwarden.ui.util.Text
Expand All @@ -39,33 +40,61 @@ import kotlinx.collections.immutable.ImmutableList
* @param items [ExpandableFabOption] buttons displayed when the FAB is expanded.
* @param label [Text] displayed when the FAB is expanded.
* @param modifier The modifier for this composable.
* @param expandableFabState [ExpandableFabIcon] displayed in the FAB.
* @param onStateChange Lambda invoked when the FAB expanded state changes.
* @param initialIsExpanded The initial state of the [ExpandableFabIcon] displayed in the FAB.
*/
@Composable
fun BitwardenExpandableFloatingActionButton(
expandableFabIcon: ExpandableFabIcon,
items: ImmutableList<ExpandableFabOption>,
modifier: Modifier = Modifier,
label: Text? = null,
initialIsExpanded: Boolean = false,
) {
var isExpanded by rememberSaveable { mutableStateOf(value = initialIsExpanded) }
BitwardenExpandableFloatingActionButton(
expandableFabIcon = expandableFabIcon,
items = items,
label = label,
isExpanded = isExpanded,
onIsExpandedChange = { isExpanded = it },
modifier = modifier,
)
}

/**
* A FAB that expands, when clicked, to display a collection of options that can be clicked.
*
* @param expandableFabIcon The icon to display and how to display it.
* @param items [ExpandableFabOption] buttons displayed when the FAB is expanded.
* @param label [Text] displayed when the FAB is expanded.
* @param modifier The modifier for this composable.
* @param isExpanded whether the FAB is in the expanded state.
* @param onIsExpandedChange Lambda invoked when the FAB expanded state changes.
*/
@Suppress("LongMethod")
@Composable
fun <T : ExpandableFabOption> BitwardenExpandableFloatingActionButton(
fun BitwardenExpandableFloatingActionButton(
expandableFabIcon: ExpandableFabIcon,
items: ImmutableList<T>,
items: ImmutableList<ExpandableFabOption>,
modifier: Modifier = Modifier,
label: Text? = null,
expandableFabState: MutableState<ExpandableFabState> = rememberExpandableFabState(),
onStateChange: (expandableFabState: ExpandableFabState) -> Unit = { },
isExpanded: Boolean,
onIsExpandedChange: (isExpanded: Boolean) -> Unit,
) {
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.Bottom,
modifier = modifier,
) {
AnimatedVisibility(
visible = expandableFabState.value.isExpanded(),
visible = isExpanded,
label = "display_fab_options_animation",
modifier = Modifier.weight(weight = 1f),
) {
LazyColumn(
modifier = Modifier
.clickable(interactionSource = null, indication = null) {
expandableFabState.value = ExpandableFabState.Collapsed
onIsExpandedChange(false)
}
.fillMaxSize(),
horizontalAlignment = Alignment.End,
Expand All @@ -78,8 +107,7 @@ fun <T : ExpandableFabOption> BitwardenExpandableFloatingActionButton(
items(items) { expandableFabOption ->
ExpandableFabOption(
onFabOptionClick = {
expandableFabState.value = expandableFabState.value.toggleValue()
onStateChange(expandableFabState.value)
onIsExpandedChange(!isExpanded)
expandableFabOption.onFabOptionClick()
},
expandableFabOption = expandableFabOption,
Expand All @@ -89,23 +117,12 @@ fun <T : ExpandableFabOption> BitwardenExpandableFloatingActionButton(
}

val rotation by animateFloatAsState(
targetValue = if (expandableFabState.value.isExpanded()) {
expandableFabIcon.iconRotation ?: 0f
} else {
0f
},
targetValue = if (isExpanded) expandableFabIcon.iconRotation else 0f,
label = "add_item_rotation",
)
ExtendedFloatingActionButton(
onClick = {
expandableFabState.value = expandableFabState.value.toggleValue()
onStateChange(expandableFabState.value)
},
expanded = if (label != null) {
!expandableFabState.value.isExpanded()
} else {
false
},
onClick = { onIsExpandedChange(!isExpanded) },
expanded = if (label != null) !isExpanded else false,
containerColor = BitwardenTheme.colorScheme.filledButton.background,
contentColor = BitwardenTheme.colorScheme.filledButton.foreground,
shape = BitwardenTheme.shapes.fab,
Expand All @@ -131,9 +148,9 @@ fun <T : ExpandableFabOption> BitwardenExpandableFloatingActionButton(
}

@Composable
private fun <T : ExpandableFabOption> ExpandableFabOption(
expandableFabOption: T,
onFabOptionClick: (option: T) -> Unit,
private fun ExpandableFabOption(
expandableFabOption: ExpandableFabOption,
onFabOptionClick: (option: ExpandableFabOption) -> Unit,
modifier: Modifier = Modifier,
) {
SmallFloatingActionButton(
Expand Down Expand Up @@ -163,53 +180,3 @@ private fun <T : ExpandableFabOption> ExpandableFabOption(
}
}
}

@Composable
private fun rememberExpandableFabState(): MutableState<ExpandableFabState> =
remember { mutableStateOf(ExpandableFabState.Collapsed) }

/**
* Represents options displayed when the FAB is expanded.
*/
abstract class ExpandableFabOption(
val label: Text,
val icon: IconData.Local,
val onFabOptionClick: () -> Unit,
)

/**
* Models data for an expandable FAB icon.
*/
data class ExpandableFabIcon(
val icon: IconData.Local,
val iconRotation: Float?,
)

/**
* Models the state of the expandable FAB.
*/
sealed class ExpandableFabState {
/**
* Indicates if the FAB is expanded.
*/
fun isExpanded(): Boolean = this == Expanded

/**
* Invert the state of the FAB.
*/
fun toggleValue(): ExpandableFabState = if (isExpanded()) {
Collapsed
} else {
Expanded
}

/**
* Indicates the FAB is collapsed.
*/
data object Collapsed : ExpandableFabState()

/**
* Indicates the FAB is expanded.
*/
data object Expanded : ExpandableFabState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.bitwarden.ui.platform.components.fab.model

import com.bitwarden.ui.platform.components.icon.model.IconData

/**
* Models data for an expandable FAB icon.
*/
data class ExpandableFabIcon(
val icon: IconData.Local,
val iconRotation: Float,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.bitwarden.ui.platform.components.fab.model

import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.util.Text

/**
* Represents options displayed when the FAB is expanded.
*/
data class ExpandableFabOption(
val label: Text,
val icon: IconData.Local,
val onFabOptionClick: () -> Unit,
)
Loading