Display UI in RTL languages correctly
This commit is contained in:
parent
a62a6ba295
commit
5ec54e2445
@ -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>
|
||||||
|
@ -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)}
|
||||||
<IconChevronRight />
|
|
||||||
|
{isRtl(i18n.language) ? (
|
||||||
|
<IconChevronLeft />
|
||||||
|
) : (
|
||||||
|
<IconChevronRight />
|
||||||
|
)}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
|
@ -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();
|
||||||
|
@ -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>
|
||||||
|
@ -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) => (
|
||||||
|
@ -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
2
src/i18n/utils/rtl.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
const rtlLanguages = ["ar", "he", "fa", "ps", "ur"];
|
||||||
|
export const isRtl = (language) => rtlLanguages.includes(language);
|
Loading…
Reference in New Issue
Block a user