fix: mirror the Contacts drawer, search fields and Files chevrons correctly in right-to-left languages

This commit is contained in:
Linus Rath
2026-10-28 04:21:47 +01:00
parent e53578454a
commit 46eb9cf42d
7 changed files with 17 additions and 17 deletions
+1 -1
View File
@@ -988,7 +988,7 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
<div className="space-y-3">
{addresses.map((addr, i) => (
<div key={i} className="rounded-md border border-border/60 bg-muted/20 p-3 space-y-2 relative">
<Button type="button" variant="ghost" size="icon" onClick={() => setAddresses(addresses.filter((_, j) => j !== i))} className="h-6 w-6 absolute top-2 right-2">
<Button type="button" variant="ghost" size="icon" onClick={() => setAddresses(addresses.filter((_, j) => j !== i))} className="h-6 w-6 absolute top-2 end-2">
<X className="w-3 h-3" />
</Button>
<Input value={addr.street} onChange={(e) => { const n = [...addresses]; n[i] = { ...n[i], street: e.target.value }; setAddresses(n); }} placeholder={t("street")} />
+1 -1
View File
@@ -116,7 +116,7 @@ export function ContactGroupForm({
{t("groups.members_label")} ({selectedIds.size})
</label>
<div className="relative mb-2">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Search className="absolute start-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
placeholder={t("groups.search_members")}
value={memberSearch}
+3 -3
View File
@@ -403,7 +403,7 @@ export function ContactList({
{hasSelection ? <CheckSquare className="w-4 h-4" /> : <Square className="w-4 h-4" />}
</button>
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Search className="absolute start-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
ref={searchRef}
type="text"
@@ -416,7 +416,7 @@ export function ContactList({
<button
type="button"
onClick={() => onSearchChange("")}
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
className="absolute end-2 top-1/2 -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
aria-label={t("clear_search")}
>
<X className="w-4 h-4" />
@@ -437,7 +437,7 @@ export function ContactList({
>
<Filter className="w-4 h-4" />
{!filtersOpen && activeFilters > 0 && (
<span className="absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 text-[10px] font-bold rounded-full bg-primary text-primary-foreground">
<span className="absolute -top-1 -end-1 flex items-center justify-center w-4 h-4 text-[10px] font-bold rounded-full bg-primary text-primary-foreground">
{activeFilters}
</span>
)}
+4 -4
View File
@@ -155,7 +155,7 @@ export function DirectoryBrowser({
{pageAllSelected ? <CheckSquare className="w-4 h-4" /> : <Square className="w-4 h-4" />}
</button>
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Search className="absolute start-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
ref={searchRef}
type="text"
@@ -169,7 +169,7 @@ export function DirectoryBrowser({
<button
type="button"
onClick={() => { setQuery(""); setPage(0); }}
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
className="absolute end-2 top-1/2 -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
aria-label={tContacts("clear_search")}
>
<X className="w-4 h-4" />
@@ -258,7 +258,7 @@ export function DirectoryBrowser({
className="p-1.5 rounded-md hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:hover:bg-transparent"
aria-label={t("previous_page")}
>
<ChevronLeft className="w-4 h-4 rtl:rotate-180" />
<ChevronLeft className="w-4 h-4" />
</button>
<span className="tabular-nums">{t("page", { page: currentPage + 1, count: pageCount })}</span>
<button
@@ -268,7 +268,7 @@ export function DirectoryBrowser({
className="p-1.5 rounded-md hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:hover:bg-transparent"
aria-label={t("next_page")}
>
<ChevronRight className="w-4 h-4 rtl:rotate-180" />
<ChevronRight className="w-4 h-4" />
</button>
</div>
)}
+2 -2
View File
@@ -144,7 +144,7 @@ export function FilePickerDialog({ client, onClose, onPick }: FilePickerDialogPr
<span className="block truncate">{name}</span>
{detail && <span className="block truncate text-xs text-muted-foreground">{detail}</span>}
</span>
<ChevronRight className="w-4 h-4 shrink-0 text-muted-foreground rtl:rotate-180" />
<ChevronRight className="w-4 h-4 shrink-0 text-muted-foreground" />
</button>
);
@@ -276,7 +276,7 @@ export function FilePickerDialog({ client, onClose, onPick }: FilePickerDialogPr
</button>
{trail.map((crumb, i) => (
<span key={crumb.id} className="flex items-center gap-1 min-w-0">
<ChevronRight className="w-3.5 h-3.5 shrink-0 text-muted-foreground rtl:rotate-180" />
<ChevronRight className="w-3.5 h-3.5 shrink-0 text-muted-foreground" />
<button
type="button"
onClick={() => setTrail(trail.slice(0, i + 1))}
+3 -3
View File
@@ -193,7 +193,7 @@ export function FilesHeader({
const isLast = crumb === crumbs[crumbs.length - 1];
return (
<li key={`${crumb.id ?? "root"}:${crumb.path}`} className="flex items-center min-w-0 shrink-0 last:shrink">
{index > 0 && <ChevronRight className="w-3.5 h-3.5 text-muted-foreground shrink-0 rtl:rotate-180" aria-hidden />}
{index > 0 && <ChevronRight className="w-3.5 h-3.5 text-muted-foreground shrink-0" aria-hidden />}
<button
type="button"
onClick={() => onNavigateCrumb(crumb)}
@@ -273,7 +273,7 @@ export function FilesHeader({
{visible.slice(0, 1).map((c) => renderCrumb(c, 0))}
{accountLabel && (
<li className="flex items-center min-w-0 shrink-0">
<ChevronRight className="w-3.5 h-3.5 text-muted-foreground shrink-0 rtl:rotate-180" aria-hidden />
<ChevronRight className="w-3.5 h-3.5 text-muted-foreground shrink-0" aria-hidden />
<button
type="button"
onClick={onNavigateAccountRoot}
@@ -285,7 +285,7 @@ export function FilesHeader({
)}
{hidden.length > 0 && (
<li className="flex items-center shrink-0">
<ChevronRight className="w-3.5 h-3.5 text-muted-foreground shrink-0 rtl:rotate-180" aria-hidden />
<ChevronRight className="w-3.5 h-3.5 text-muted-foreground shrink-0" aria-hidden />
<button
type="button"
className="px-1.5 py-1 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted"
+3 -3
View File
@@ -214,7 +214,7 @@ export function FilesSidebar({
className="p-0.5 -ms-1 rounded hover:bg-foreground/10 text-muted-foreground shrink-0"
aria-label={isOpen ? t("collapse_folder", { name: folder.name }) : t("expand_folder", { name: folder.name })}
>
{isOpen ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5 rtl:rotate-180" />}
{isOpen ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5" />}
</button>
) : (
<span className="w-3.5 -ms-1 shrink-0" />
@@ -244,7 +244,7 @@ export function FilesSidebar({
aria-expanded={!collapsed[key]}
>
{collapsed[key]
? <ChevronRight className="w-3 h-3 text-muted-foreground rtl:rotate-180" />
? <ChevronRight className="w-3 h-3 text-muted-foreground" />
: <ChevronDown className="w-3 h-3 text-muted-foreground" />}
{icon}
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider truncate">{label}</span>
@@ -315,7 +315,7 @@ export function FilesSidebar({
className="p-0.5 -ms-1 rounded hover:bg-foreground/10 text-muted-foreground shrink-0"
aria-label={collapsed.tree ? t("expand_folder", { name: t("my_files") }) : t("collapse_folder", { name: t("my_files") })}
>
{collapsed.tree ? <ChevronRight className="w-3.5 h-3.5 rtl:rotate-180" /> : <ChevronDown className="w-3.5 h-3.5" />}
{collapsed.tree ? <ChevronRight className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
</button>
) : (
<span className="w-3.5 -ms-1 shrink-0" />