Skip to content
Draft
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 @@ -24,6 +24,7 @@ import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.bitwarden.ui.platform.util.withForcedLtrDirection

/**
* The verification code item displayed to the user.
Expand Down Expand Up @@ -107,7 +108,7 @@ fun VaultVerificationCodeItem(
if (!hideAuthCode) {
Text(
text = authCode.chunked(3).joinToString(" "),
style = BitwardenTheme.typography.sensitiveInfoSmall,
style = BitwardenTheme.typography.sensitiveInfoSmall.withForcedLtrDirection(),
color = BitwardenTheme.colorScheme.text.primary,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.bitwarden.ui.platform.util.withForcedLtrDirection

/**
* The verification code item displayed to the user.
Expand Down Expand Up @@ -155,7 +156,7 @@ fun VaultVerificationCodeItem(
Text(
modifier = Modifier.testTag(tag = "AuthCode"),
text = authCode.chunked(size = 3).joinToString(separator = " "),
style = BitwardenTheme.typography.sensitiveInfoSmall,
style = BitwardenTheme.typography.sensitiveInfoSmall.withForcedLtrDirection(),
color = BitwardenTheme.colorScheme.text.primary,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import com.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformat
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.bitwarden.ui.platform.util.withForcedLtrDirection

/**
* Represents a Bitwarden-styled password field that hoists show/hide password state to the caller.
Expand Down Expand Up @@ -155,7 +156,7 @@ fun BitwardenPasswordField(
var focused by remember { mutableStateOf(value = false) }
TextField(
colors = bitwardenTextFieldColors(),
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
textStyle = BitwardenTheme.typography.sensitiveInfoSmall.withForcedLtrDirection(),
label = label?.let {
{
Row(verticalAlignment = Alignment.CenterVertically) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.bitwarden.ui.platform.util

import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.LayoutDirection
import com.bitwarden.annotation.OmitFromCoverage

/**
* Returns a [TextStyle] that forces left-to-right text direction while maintaining
* locale-aware alignment.
*
* This extension is designed for sensitive alphanumeric content (passwords, TOTP codes)
* that must always read left-to-right regardless of system locale, but should align
* according to the layout direction (right-aligned in RTL locales, left-aligned in LTR).
*
* **Implementation:**
* - Sets `textDirection = TextDirection.Ltr` to force LTR reading order
* - Sets `textAlign` conditionally:
* - `TextAlign.End` in RTL layouts (aligns to right side)
* - `TextAlign.Start` in LTR layouts (aligns to left side)
*
* **Use cases:**
* - Password fields that should read "Pass123!" not "!321ssaP" in RTL locales
* - TOTP verification codes that should read "123 456" not "654 321"
* - Any alphanumeric content requiring LTR reading with locale-aware positioning
*

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great docs!

* @return A merged [TextStyle] with forced LTR direction and locale-aware alignment.
*/
@OmitFromCoverage
@Composable
fun TextStyle.withForcedLtrDirection(
layoutDirection: LayoutDirection = LocalLayoutDirection.current,
): TextStyle {
return merge(
TextStyle(
textDirection = TextDirection.Ltr,
textAlign = when (layoutDirection) {
LayoutDirection.Rtl -> TextAlign.Right
LayoutDirection.Ltr -> TextAlign.Left
},
),
)
}
Loading