mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
fix: mirror the Contacts drawer, search fields and Files chevrons correctly in right-to-left languages
This commit is contained in:
@@ -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")} />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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))}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user