feat(i18n): Hebrew locale + full RTL support
This commit is contained in:
@@ -821,8 +821,8 @@ export function FileBrowser({
|
||||
const SortIndicator = ({ column }: { column: SortKey }) => {
|
||||
if (sortKey !== column) return null;
|
||||
return sortDir === "asc"
|
||||
? <ArrowUp className="w-3 h-3 inline ml-1" />
|
||||
: <ArrowDown className="w-3 h-3 inline ml-1" />;
|
||||
? <ArrowUp className="w-3 h-3 inline ms-1" />
|
||||
: <ArrowDown className="w-3 h-3 inline ms-1" />;
|
||||
};
|
||||
|
||||
// Keyboard shortcuts
|
||||
@@ -942,7 +942,7 @@ export function FileBrowser({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 -ml-2"
|
||||
className="h-8 w-8 -ms-2"
|
||||
onClick={() => setNarrowSidebarOpen((v) => !v)}
|
||||
aria-label={t("open_folder_tree")}
|
||||
>
|
||||
@@ -982,7 +982,7 @@ export function FileBrowser({
|
||||
className="h-8"
|
||||
onClick={() => onBatchDownload([...selectedResources].filter(n => !resources.find(r => r.name === n)?.isDirectory))}
|
||||
>
|
||||
<Download className="w-4 h-4 mr-1" />
|
||||
<Download className="w-4 h-4 me-1" />
|
||||
{t("download")} ({[...selectedResources].filter(n => !resources.find(r => r.name === n)?.isDirectory).length})
|
||||
</Button>
|
||||
<Button
|
||||
@@ -991,7 +991,7 @@ export function FileBrowser({
|
||||
className="h-8 text-destructive hover:text-destructive"
|
||||
onClick={() => onBatchDelete([...selectedResources])}
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-1" />
|
||||
<Trash2 className="w-4 h-4 me-1" />
|
||||
{t("delete")} ({selectedResources.size})
|
||||
</Button>
|
||||
</>
|
||||
@@ -1003,7 +1003,7 @@ export function FileBrowser({
|
||||
className="h-8"
|
||||
onClick={onPaste}
|
||||
>
|
||||
<Clipboard className="w-4 h-4 mr-1" />
|
||||
<Clipboard className="w-4 h-4 me-1" />
|
||||
{t("paste")} ({clipboard.names.length})
|
||||
</Button>
|
||||
)}
|
||||
@@ -1157,7 +1157,7 @@ export function FileBrowser({
|
||||
className="h-7 text-destructive hover:text-destructive shrink-0"
|
||||
onClick={onRefresh}
|
||||
>
|
||||
<RefreshCw className="w-3.5 h-3.5 mr-1" />
|
||||
<RefreshCw className="w-3.5 h-3.5 me-1" />
|
||||
{t("retry")}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1181,12 +1181,12 @@ export function FileBrowser({
|
||||
<span className="truncate">
|
||||
{t("uploading")} {uploadProgress.name}
|
||||
{uploadProgress.totalFiles > 1 && (
|
||||
<span className="text-muted-foreground ml-1">
|
||||
<span className="text-muted-foreground ms-1">
|
||||
({uploadProgress.current}/{uploadProgress.totalFiles})
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="ml-auto tabular-nums shrink-0 flex items-center gap-2">
|
||||
<span className="ms-auto tabular-nums shrink-0 flex items-center gap-2">
|
||||
{uploadProgress.total > 0
|
||||
? `${Math.round((uploadProgress.loaded / uploadProgress.total) * 100)}%`
|
||||
: "…"}
|
||||
@@ -1267,7 +1267,7 @@ export function FileBrowser({
|
||||
)}
|
||||
{/* Favorites & Recent sidebar (when layout is inline) */}
|
||||
{folderLayout === "inline" && (favorites.length > 0 || recentFiles.length > 0) && (
|
||||
<div className="w-48 border-r border-border bg-background overflow-y-auto shrink-0 hidden lg:block">
|
||||
<div className="w-48 border-e border-border bg-background overflow-y-auto shrink-0 hidden lg:block">
|
||||
{favorites.length > 0 && (
|
||||
<div className="p-3">
|
||||
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-2 flex items-center gap-1">
|
||||
@@ -1280,7 +1280,7 @@ export function FileBrowser({
|
||||
key={fav}
|
||||
onClick={() => onNavigate(fav)}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-left",
|
||||
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-start",
|
||||
currentPath === fav && "bg-muted font-medium"
|
||||
)}
|
||||
>
|
||||
@@ -1301,7 +1301,7 @@ export function FileBrowser({
|
||||
{recentFiles.slice(0, 10).map((recent) => (
|
||||
<button
|
||||
key={recent.id}
|
||||
className="w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-start"
|
||||
title={recent.name}
|
||||
>
|
||||
<File className="w-3.5 h-3.5 text-muted-foreground shrink-0" />
|
||||
@@ -1322,14 +1322,14 @@ export function FileBrowser({
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-muted/50 sticky top-0 z-10">
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left px-4 py-2 font-medium text-muted-foreground">
|
||||
<th className="text-start px-4 py-2 font-medium text-muted-foreground">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-4 h-4" />
|
||||
{t("name")}
|
||||
</div>
|
||||
</th>
|
||||
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">{t("size")}</th>
|
||||
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">{t("modified")}</th>
|
||||
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">{t("size")}</th>
|
||||
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">{t("modified")}</th>
|
||||
<th className="w-10 px-2 py-2" />
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1354,7 +1354,7 @@ export function FileBrowser({
|
||||
key={`__account__:${acc.accountId}`}
|
||||
onClick={() => onSelectAccount(acc.accountId)}
|
||||
title={acc.email}
|
||||
className="flex items-center gap-3 p-3 rounded-lg border border-border hover:bg-muted/50 transition-colors text-left min-w-0"
|
||||
className="flex items-center gap-3 p-3 rounded-lg border border-border hover:bg-muted/50 transition-colors text-start min-w-0"
|
||||
>
|
||||
<Avatar
|
||||
name={acc.label}
|
||||
@@ -1514,7 +1514,7 @@ export function FileBrowser({
|
||||
>
|
||||
<thead className="bg-muted/50 sticky top-0 z-10">
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left px-4 py-2 font-medium text-muted-foreground">
|
||||
<th className="text-start px-4 py-2 font-medium text-muted-foreground">
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -1530,13 +1530,13 @@ export function FileBrowser({
|
||||
</button>
|
||||
</div>
|
||||
</th>
|
||||
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">
|
||||
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">
|
||||
<button onClick={() => handleSortClick("size")} className="hover:text-foreground transition-colors">
|
||||
{t("size")}
|
||||
<SortIndicator column="size" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">
|
||||
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">
|
||||
<button onClick={() => handleSortClick("modified")} className="hover:text-foreground transition-colors">
|
||||
{t("modified")}
|
||||
<SortIndicator column="modified" />
|
||||
@@ -1681,7 +1681,7 @@ export function FileBrowser({
|
||||
>
|
||||
{!resources.find(r => r.name === contextMenu.name)?.isDirectory && isPreviewable(contextMenu.name) && (
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
if (isImageFile(contextMenu.name)) {
|
||||
onPreviewImage(contextMenu.name);
|
||||
@@ -1697,7 +1697,7 @@ export function FileBrowser({
|
||||
)}
|
||||
{!resources.find(r => r.name === contextMenu.name)?.isDirectory && (
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onDownload(contextMenu.name);
|
||||
setContextMenu(null);
|
||||
@@ -1708,7 +1708,7 @@ export function FileBrowser({
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onCut([contextMenu.name]);
|
||||
setContextMenu(null);
|
||||
@@ -1718,7 +1718,7 @@ export function FileBrowser({
|
||||
{t("cut")}
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onCopy([contextMenu.name]);
|
||||
setContextMenu(null);
|
||||
@@ -1729,7 +1729,7 @@ export function FileBrowser({
|
||||
</button>
|
||||
{clipboard && (
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onPaste();
|
||||
setContextMenu(null);
|
||||
@@ -1741,7 +1741,7 @@ export function FileBrowser({
|
||||
)}
|
||||
{!resources.find(r => r.name === contextMenu.name)?.isDirectory && (
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onDuplicate(contextMenu.name);
|
||||
setContextMenu(null);
|
||||
@@ -1753,7 +1753,7 @@ export function FileBrowser({
|
||||
)}
|
||||
{canShare(resources.find(r => r.name === contextMenu.name)) && (
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
const r = resources.find(res => res.name === contextMenu.name);
|
||||
if (r) setShareTargetId(r.id);
|
||||
@@ -1766,7 +1766,7 @@ export function FileBrowser({
|
||||
)}
|
||||
<div className="h-px bg-border my-1" />
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onShowDetails(contextMenu.name);
|
||||
setContextMenu(null);
|
||||
@@ -1776,7 +1776,7 @@ export function FileBrowser({
|
||||
{t("details")}
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
setRenameTarget(contextMenu.name);
|
||||
setContextMenu(null);
|
||||
@@ -1786,7 +1786,7 @@ export function FileBrowser({
|
||||
{t("rename")}
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted text-destructive transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted text-destructive transition-colors text-start"
|
||||
onClick={() => {
|
||||
onDelete(contextMenu.name);
|
||||
setContextMenu(null);
|
||||
@@ -1808,7 +1808,7 @@ export function FileBrowser({
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
setShowNewFolder(true);
|
||||
setEmptyContextMenu(null);
|
||||
@@ -1818,7 +1818,7 @@ export function FileBrowser({
|
||||
{t("new_folder")}
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
setShowNewTextFile(true);
|
||||
setEmptyContextMenu(null);
|
||||
@@ -1828,7 +1828,7 @@ export function FileBrowser({
|
||||
{t("new_text_file")}
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
fileInputRef.current?.click();
|
||||
setEmptyContextMenu(null);
|
||||
@@ -1838,7 +1838,7 @@ export function FileBrowser({
|
||||
{t("upload")}
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
folderInputRef.current?.click();
|
||||
setEmptyContextMenu(null);
|
||||
@@ -1851,7 +1851,7 @@ export function FileBrowser({
|
||||
<>
|
||||
<div className="h-px bg-border my-1" />
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onPaste();
|
||||
setEmptyContextMenu(null);
|
||||
@@ -1864,7 +1864,7 @@ export function FileBrowser({
|
||||
)}
|
||||
<div className="h-px bg-border my-1" />
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onRefresh();
|
||||
setEmptyContextMenu(null);
|
||||
@@ -1895,7 +1895,7 @@ export function FileBrowser({
|
||||
return (
|
||||
<button
|
||||
key={folder.id}
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
|
||||
onClick={() => {
|
||||
onNavigate(folderPath, folder.id);
|
||||
setBreadcrumbDropdown(null);
|
||||
@@ -1926,7 +1926,7 @@ export function FileBrowser({
|
||||
|
||||
{/* Details sidebar */}
|
||||
{showDetails && detailResource && (
|
||||
<div role="complementary" aria-label={t("details")} className="w-64 border-l border-border bg-background p-4 overflow-y-auto shrink-0 hidden md:block">
|
||||
<div role="complementary" aria-label={t("details")} className="w-64 border-s border-border bg-background p-4 overflow-y-auto shrink-0 hidden md:block">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-sm font-medium">{t("details")}</h3>
|
||||
<button onClick={onToggleDetails} className="text-muted-foreground hover:text-foreground">
|
||||
|
||||
Reference in New Issue
Block a user