Display UI in RTL languages correctly

This commit is contained in:
tasnim 2024-08-04 15:27:31 +03:00
parent a62a6ba295
commit 5ec54e2445
7 changed files with 60 additions and 14 deletions

View File

@ -18,6 +18,8 @@ import { useLayout, useSettings, useDiagram, useSelect } from "../../hooks";
import TableInfo from "../EditorSidePanel/TablesTab/TableInfo"; import TableInfo from "../EditorSidePanel/TablesTab/TableInfo";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { dbToTypes } from "../../data/datatypes"; import { dbToTypes } from "../../data/datatypes";
import { isRtl } from "../../i18n/utils/rtl";
import i18n from "../../i18n/i18n";
export default function Table(props) { export default function Table(props) {
const [hoveredField, setHoveredField] = useState(-1); const [hoveredField, setHoveredField] = useState(-1);
@ -84,6 +86,7 @@ export default function Table(props) {
? "border-solid border-blue-500" ? "border-solid border-blue-500"
: "border-zinc-500" : "border-zinc-500"
}`} }`}
style={{ direction: "ltr" }}
> >
<div <div
className="h-[10px] w-full rounded-t-md" className="h-[10px] w-full rounded-t-md"
@ -190,7 +193,10 @@ export default function Table(props) {
key={i} key={i}
content={ content={
<div className="popover-theme"> <div className="popover-theme">
<div className="flex justify-between items-center pb-2"> <div
className="flex justify-between items-center pb-2"
style={{ direction: "ltr" }}
>
<p className="me-4 font-bold">{e.name}</p> <p className="me-4 font-bold">{e.name}</p>
<p className="ms-4"> <p className="ms-4">
{e.type + {e.type +
@ -235,6 +241,11 @@ export default function Table(props) {
} }
position="right" position="right"
showArrow showArrow
style={
isRtl(i18n.language)
? { direction: "rtl" }
: { direction: "ltr" }
}
> >
{field(e, i)} {field(e, i)}
</Popover> </Popover>

View File

@ -2,6 +2,7 @@ import { useState } from "react";
import { import {
IconCaretdown, IconCaretdown,
IconChevronRight, IconChevronRight,
IconChevronLeft,
IconChevronUp, IconChevronUp,
IconChevronDown, IconChevronDown,
IconSaveStroked, IconSaveStroked,
@ -68,6 +69,7 @@ import { useTranslation } from "react-i18next";
import { exportSQL } from "../../utils/exportSQL"; import { exportSQL } from "../../utils/exportSQL";
import { databases } from "../../data/databases"; import { databases } from "../../data/databases";
import { jsonToMermaid } from "../../utils/exportAs/mermaid"; import { jsonToMermaid } from "../../utils/exportAs/mermaid";
import { isRtl } from "../../i18n/utils/rtl";
export default function ControlPanel({ export default function ControlPanel({
diagramId, diagramId,
@ -109,7 +111,7 @@ export default function ControlPanel({
const { undoStack, redoStack, setUndoStack, setRedoStack } = useUndoRedo(); const { undoStack, redoStack, setUndoStack, setRedoStack } = useUndoRedo();
const { selectedElement, setSelectedElement } = useSelect(); const { selectedElement, setSelectedElement } = useSelect();
const { transform, setTransform } = useTransform(); const { transform, setTransform } = useTransform();
const { t } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const invertLayout = (component) => const invertLayout = (component) =>
@ -1374,7 +1376,10 @@ export default function ControlPanel({
function toolbar() { function toolbar() {
return ( return (
<div className="py-1.5 px-5 flex justify-between items-center rounded-xl my-1 sm:mx-1 xl:mx-6 select-none overflow-hidden toolbar-theme"> <div
className="py-1.5 px-5 flex justify-between items-center rounded-xl my-1 sm:mx-1 xl:mx-6 select-none overflow-hidden toolbar-theme"
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
>
<div className="flex justify-start items-center"> <div className="flex justify-start items-center">
<LayoutDropdown /> <LayoutDropdown />
<Divider layout="vertical" margin="8px" /> <Divider layout="vertical" margin="8px" />
@ -1382,7 +1387,9 @@ export default function ControlPanel({
style={{ width: "240px" }} style={{ width: "240px" }}
position="bottomLeft" position="bottomLeft"
render={ render={
<Dropdown.Menu> <Dropdown.Menu
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
>
<Dropdown.Item <Dropdown.Item
onClick={fitWindow} onClick={fitWindow}
style={{ display: "flex", justifyContent: "space-between" }} style={{ display: "flex", justifyContent: "space-between" }}
@ -1562,7 +1569,10 @@ export default function ControlPanel({
function header() { function header() {
return ( return (
<nav className="flex justify-between pt-1 items-center whitespace-nowrap"> <nav
className="flex justify-between pt-1 items-center whitespace-nowrap"
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
>
<div className="flex justify-start items-center"> <div className="flex justify-start items-center">
<Link to="/"> <Link to="/">
<img <img
@ -1608,7 +1618,10 @@ export default function ControlPanel({
<Dropdown <Dropdown
key={category} key={category}
position="bottomLeft" position="bottomLeft"
style={{ width: "240px" }} style={{
width: "240px",
direction: isRtl(i18n.language) ? "rtl" : "ltr",
}}
render={ render={
<Dropdown.Menu> <Dropdown.Menu>
{Object.keys(menu[category]).map((item, index) => { {Object.keys(menu[category]).map((item, index) => {
@ -1642,7 +1655,12 @@ export default function ControlPanel({
onClick={menu[category][item].function} onClick={menu[category][item].function}
> >
{t(item)} {t(item)}
{isRtl(i18n.language) ? (
<IconChevronLeft />
) : (
<IconChevronRight /> <IconChevronRight />
)}
</Dropdown.Item> </Dropdown.Item>
</Dropdown> </Dropdown>
); );

View File

@ -7,6 +7,8 @@ import { Dropdown } from "@douyinfe/semi-ui";
import { useFullscreen, useLayout } from "../../hooks"; import { useFullscreen, useLayout } from "../../hooks";
import { enterFullscreen, exitFullscreen } from "../../utils/fullscreen"; import { enterFullscreen, exitFullscreen } from "../../utils/fullscreen";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { isRtl } from "../../i18n/utils/rtl";
import i18n from "../../i18n/i18n";
export default function LayoutDropdown() { export default function LayoutDropdown() {
const fullscreen = useFullscreen(); const fullscreen = useFullscreen();
@ -19,7 +21,10 @@ export default function LayoutDropdown() {
return ( return (
<Dropdown <Dropdown
position="bottomLeft" position="bottomLeft"
style={{ width: "180px" }} style={{
width: "180px",
direction: isRtl(i18n.language) ? "rtl" : "ltr",
}}
render={ render={
<Dropdown.Menu> <Dropdown.Menu>
<Dropdown.Item <Dropdown.Item
@ -48,9 +53,7 @@ export default function LayoutDropdown() {
</Dropdown.Item> </Dropdown.Item>
<Dropdown.Divider /> <Dropdown.Divider />
<Dropdown.Item <Dropdown.Item
icon={ icon={fullscreen ? <IconCheckboxTick /> : <div className="px-2" />}
fullscreen ? <IconCheckboxTick /> : <div className="px-2" />
}
onClick={() => { onClick={() => {
if (fullscreen) { if (fullscreen) {
exitFullscreen(); exitFullscreen();

View File

@ -37,6 +37,7 @@ import { githubLight } from "@uiw/codemirror-theme-github";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { importSQL } from "../../../utils/importSQL"; import { importSQL } from "../../../utils/importSQL";
import { databases } from "../../../data/databases"; import { databases } from "../../../data/databases";
import { isRtl } from "../../../i18n/utils/rtl";
const languageExtension = { const languageExtension = {
sql: [sql()], sql: [sql()],
@ -53,7 +54,7 @@ export default function Modal({
setExportData, setExportData,
importDb, importDb,
}) { }) {
const { t } = useTranslation(); const { t, i18n } = useTranslation();
const { setTables, setRelationships, database, setDatabase } = useDiagram(); const { setTables, setRelationships, database, setDatabase } = useDiagram();
const { setNotes } = useNotes(); const { setNotes } = useNotes();
const { setAreas } = useAreas(); const { setAreas } = useAreas();
@ -324,6 +325,7 @@ export default function Modal({
return ( return (
<SemiUIModal <SemiUIModal
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
title={getModalTitle(modal)} title={getModalTitle(modal)}
visible={modal !== MODAL.NONE} visible={modal !== MODAL.NONE}
onOk={getModalOnOk} onOk={getModalOnOk}
@ -362,7 +364,11 @@ export default function Modal({
}} }}
cancelText={t("cancel")} cancelText={t("cancel")}
width={modal === MODAL.NEW || modal === MODAL.OPEN ? 740 : 600} width={modal === MODAL.NEW || modal === MODAL.OPEN ? 740 : 600}
bodyStyle={{ maxHeight: window.innerHeight - 280, overflow: "auto" }} bodyStyle={{
maxHeight: window.innerHeight - 280,
overflow: "auto",
direction: "ltr",
}}
> >
{getModalBody()} {getModalBody()}
</SemiUIModal> </SemiUIModal>

View File

@ -11,6 +11,8 @@ import { useTranslation } from "react-i18next";
import { useMemo } from "react"; import { useMemo } from "react";
import { databases } from "../../data/databases"; import { databases } from "../../data/databases";
import EnumsTab from "./EnumsTab/EnumsTab"; import EnumsTab from "./EnumsTab/EnumsTab";
import { isRtl } from "../../i18n/utils/rtl";
import i18n from "../../i18n/i18n";
export default function SidePanel({ width, resize, setResize }) { export default function SidePanel({ width, resize, setResize }) {
const { layout } = useLayout(); const { layout } = useLayout();
@ -46,7 +48,7 @@ export default function SidePanel({ width, resize, setResize }) {
}); });
} }
return tabs; return isRtl(i18n.language) ? tabs.reverse() : tabs;
}, [t, database]); }, [t, database]);
return ( return (
@ -64,6 +66,7 @@ export default function SidePanel({ width, resize, setResize }) {
setSelectedElement((prev) => ({ ...prev, currentTab: key })) setSelectedElement((prev) => ({ ...prev, currentTab: key }))
} }
collapsible collapsible
tabBarStyle={{ direction: "ltr" }}
> >
{tabList.length && {tabList.length &&
tabList.map((tab) => ( tabList.map((tab) => (

View File

@ -22,6 +22,8 @@ import FloatingControls from "./FloatingControls";
import { Modal } from "@douyinfe/semi-ui"; import { Modal } from "@douyinfe/semi-ui";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { databases } from "../data/databases"; import { databases } from "../data/databases";
import { isRtl } from "../i18n/utils/rtl";
import i18n from "../i18n/i18n";
export default function WorkSpace() { export default function WorkSpace() {
const [id, setId] = useState(0); const [id, setId] = useState(0);
@ -358,6 +360,7 @@ export default function WorkSpace() {
// https://stackoverflow.com/a/70976017/1137077 // https://stackoverflow.com/a/70976017/1137077
e.target.releasePointerCapture(e.pointerId); e.target.releasePointerCapture(e.pointerId);
}} }}
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
> >
{layout.sidebar && ( {layout.sidebar && (
<SidePanel resize={resize} setResize={setResize} width={width} /> <SidePanel resize={resize} setResize={setResize} width={width} />

2
src/i18n/utils/rtl.js Normal file
View File

@ -0,0 +1,2 @@
const rtlLanguages = ["ar", "he", "fa", "ps", "ur"];
export const isRtl = (language) => rtlLanguages.includes(language);