Merge branch 'main' into select-db
# Conflicts: # src/i18n/locales/en.js # src/utils/exportSQL/generic.js
This commit is contained in:
commit
322bb6e988
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
open_collective: drawdb
|
@ -247,7 +247,7 @@ export default function Todo() {
|
|||||||
<TextArea
|
<TextArea
|
||||||
placeholder={t("details")}
|
placeholder={t("details")}
|
||||||
onChange={(v) => updateTask(i, { details: v })}
|
onChange={(v) => updateTask(i, { details: v })}
|
||||||
value={t.details}
|
value={task.details}
|
||||||
onBlur={() => setSaveState(State.SAVING)}
|
onBlur={() => setSaveState(State.SAVING)}
|
||||||
></TextArea>
|
></TextArea>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -15,7 +15,7 @@ export default function AreasTab() {
|
|||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
<div>
|
<div>
|
||||||
<Button icon={<IconPlus />} block onClick={addArea}>
|
<Button icon={<IconPlus />} block onClick={() => addArea()}>
|
||||||
{t("add_area")}
|
{t("add_area")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,7 +98,7 @@ export default function NoteInfo({ data, nid }) {
|
|||||||
<button
|
<button
|
||||||
key={c}
|
key={c}
|
||||||
style={{ backgroundColor: c }}
|
style={{ backgroundColor: c }}
|
||||||
className="p-3 rounded-full mx-1"
|
className="w-10 h-10 p-3 rounded-full mx-1"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUndoStack((prev) => [
|
setUndoStack((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
|
@ -3,11 +3,13 @@ import { Input, Button, Popover, Checkbox, Select } from "@douyinfe/semi-ui";
|
|||||||
import { IconMore, IconDeleteStroked } from "@douyinfe/semi-icons";
|
import { IconMore, IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||||
import { useDiagram, useUndoRedo } from "../../../hooks";
|
import { useDiagram, useUndoRedo } from "../../../hooks";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export default function IndexDetails({ data, fields, iid, tid }) {
|
export default function IndexDetails({ data, fields, iid, tid }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { tables, updateTable } = useDiagram();
|
const { tables, updateTable } = useDiagram();
|
||||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||||
|
const [editField, setEditField] = useState({});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
@ -29,11 +31,9 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
|||||||
iid: iid,
|
iid: iid,
|
||||||
undo: {
|
undo: {
|
||||||
fields: [...data.fields],
|
fields: [...data.fields],
|
||||||
name: `${data.fields.join("_")}_index`,
|
|
||||||
},
|
},
|
||||||
redo: {
|
redo: {
|
||||||
fields: [...value],
|
fields: [...value],
|
||||||
name: `${value.join("_")}_index`,
|
|
||||||
},
|
},
|
||||||
message: t("edit_table", {
|
message: t("edit_table", {
|
||||||
tableName: tables[tid].name,
|
tableName: tables[tid].name,
|
||||||
@ -48,7 +48,6 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
|||||||
? {
|
? {
|
||||||
...index,
|
...index,
|
||||||
fields: [...value],
|
fields: [...value],
|
||||||
name: `${value.join("_")}_index`,
|
|
||||||
}
|
}
|
||||||
: index,
|
: index,
|
||||||
),
|
),
|
||||||
@ -59,7 +58,48 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
|||||||
content={
|
content={
|
||||||
<div className="px-1 popover-theme">
|
<div className="px-1 popover-theme">
|
||||||
<div className="font-semibold mb-1">{t("name")}: </div>
|
<div className="font-semibold mb-1">{t("name")}: </div>
|
||||||
<Input value={data.name} placeholder={t("name")} disabled />
|
<Input
|
||||||
|
value={data.name}
|
||||||
|
placeholder={t("name")}
|
||||||
|
validateStatus={data.name.trim() === "" ? "error" : "default"}
|
||||||
|
onFocus={() =>
|
||||||
|
setEditField({
|
||||||
|
name: data.name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
updateTable(tid, {
|
||||||
|
indices: tables[tid].indices.map((index) =>
|
||||||
|
index.id === iid
|
||||||
|
? {
|
||||||
|
...index,
|
||||||
|
name: value,
|
||||||
|
}
|
||||||
|
: index,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onBlur={(e) => {
|
||||||
|
if (e.target.value === editField.name) return;
|
||||||
|
setUndoStack((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
action: Action.EDIT,
|
||||||
|
element: ObjectType.TABLE,
|
||||||
|
component: "index",
|
||||||
|
tid: tid,
|
||||||
|
iid: iid,
|
||||||
|
undo: editField,
|
||||||
|
redo: { name: e.target.value },
|
||||||
|
message: t("edit_table", {
|
||||||
|
tableName: tables[tid].name,
|
||||||
|
extra: "[index]",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setRedoStack([]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className="flex justify-between items-center my-3">
|
<div className="flex justify-between items-center my-3">
|
||||||
<div className="font-medium">{t("unique")}</div>
|
<div className="font-medium">{t("unique")}</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -133,7 +173,7 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
{t("delete")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,64 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelect } from "../../../hooks";
|
import { useSelect } from "../../../hooks";
|
||||||
import { AutoComplete } from "@douyinfe/semi-ui";
|
import { TreeSelect } from "@douyinfe/semi-ui";
|
||||||
import { IconSearch } from "@douyinfe/semi-icons";
|
import { IconSearch } from "@douyinfe/semi-icons";
|
||||||
import { ObjectType } from "../../../data/constants";
|
import { ObjectType } from "../../../data/constants";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export default function SearchBar({ tables }) {
|
export default function SearchBar({ tables }) {
|
||||||
const { setSelectedElement } = useSelect();
|
const { setSelectedElement } = useSelect();
|
||||||
const [searchText, setSearchText] = useState("");
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const filteredTable = useMemo(
|
|
||||||
() => tables.map((t) => t.name).filter((i) => i.includes(searchText)),
|
const treeData = useMemo(() => {
|
||||||
[tables, searchText],
|
return tables.map(({ id, name: parentName, fields }, i) => {
|
||||||
);
|
const children = fields.map(({ name }, j) => ({
|
||||||
|
tableId: id,
|
||||||
|
id: `${j}`,
|
||||||
|
label: name,
|
||||||
|
value: name,
|
||||||
|
key: `${i}-${j}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableId: id,
|
||||||
|
id: `${i}`,
|
||||||
|
label: parentName,
|
||||||
|
value: parentName,
|
||||||
|
key: `${i}`,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [tables]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AutoComplete
|
<TreeSelect
|
||||||
data={filteredTable}
|
searchPosition="trigger"
|
||||||
value={searchText}
|
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
|
||||||
showClear
|
treeData={treeData}
|
||||||
prefix={<IconSearch />}
|
prefix={<IconSearch />}
|
||||||
placeholder={t("search")}
|
|
||||||
emptyContent={<div className="p-3 popover-theme">{t("not_found")}</div>}
|
emptyContent={<div className="p-3 popover-theme">{t("not_found")}</div>}
|
||||||
onChange={(v) => setSearchText(v)}
|
filterTreeNode
|
||||||
onSelect={(v) => {
|
placeholder={t("search")}
|
||||||
const { id } = tables.find((t) => t.name === v);
|
onChange={(node) => {
|
||||||
|
const { tableId, id, children } = node;
|
||||||
|
|
||||||
setSelectedElement((prev) => ({
|
setSelectedElement((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
id: id,
|
id: tableId,
|
||||||
open: true,
|
open: true,
|
||||||
element: ObjectType.TABLE,
|
element: ObjectType.TABLE,
|
||||||
}));
|
}));
|
||||||
document
|
document
|
||||||
.getElementById(`scroll_table_${id}`)
|
.getElementById(`scroll_table_${tableId}`)
|
||||||
.scrollIntoView({ behavior: "smooth" });
|
.scrollIntoView({ behavior: "smooth" });
|
||||||
|
|
||||||
|
if (!children) {
|
||||||
|
document
|
||||||
|
.getElementById(`scroll_table_${tableId}_input_${id}`)
|
||||||
|
.focus();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
onChangeWithObject
|
||||||
className="w-full"
|
className="w-full"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -20,8 +20,9 @@ export default function TableField({ data, tid, index }) {
|
|||||||
<Row gutter={6} className="hover-1 my-2">
|
<Row gutter={6} className="hover-1 my-2">
|
||||||
<Col span={7}>
|
<Col span={7}>
|
||||||
<Input
|
<Input
|
||||||
|
id={`scroll_table_${tid}_input_${index}`}
|
||||||
value={data.name}
|
value={data.name}
|
||||||
validateStatus={data.name === "" ? "error" : "default"}
|
validateStatus={data.name.trim() === "" ? "error" : "default"}
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
onChange={(value) => updateField(tid, index, { name: value })}
|
onChange={(value) => updateField(tid, index, { name: value })}
|
||||||
onFocus={(e) => setEditField({ name: e.target.value })}
|
onFocus={(e) => setEditField({ name: e.target.value })}
|
||||||
|
@ -33,7 +33,7 @@ export default function TableInfo({ data }) {
|
|||||||
<div className="text-md font-semibold break-keep">{t("name")}: </div>
|
<div className="text-md font-semibold break-keep">{t("name")}: </div>
|
||||||
<Input
|
<Input
|
||||||
value={data.name}
|
value={data.name}
|
||||||
validateStatus={data.name === "" ? "error" : "default"}
|
validateStatus={data.name.trim() === "" ? "error" : "default"}
|
||||||
placeholder={t("name")}
|
placeholder={t("name")}
|
||||||
className="ms-2"
|
className="ms-2"
|
||||||
onChange={(value) => updateTable(data.id, { name: value })}
|
onChange={(value) => updateTable(data.id, { name: value })}
|
||||||
@ -290,7 +290,7 @@ export default function TableInfo({ data }) {
|
|||||||
...data.indices,
|
...data.indices,
|
||||||
{
|
{
|
||||||
id: data.indices.length,
|
id: data.indices.length,
|
||||||
name: `index_${data.indices.length}`,
|
name: `${data.name}_index_${data.indices.length}`,
|
||||||
unique: false,
|
unique: false,
|
||||||
fields: [],
|
fields: [],
|
||||||
},
|
},
|
||||||
|
@ -46,10 +46,17 @@ export default function TablesTab() {
|
|||||||
{tables.map((t) => (
|
{tables.map((t) => (
|
||||||
<div id={`scroll_table_${t.id}`} key={t.id}>
|
<div id={`scroll_table_${t.id}`} key={t.id}>
|
||||||
<Collapse.Panel
|
<Collapse.Panel
|
||||||
|
className="relative"
|
||||||
header={
|
header={
|
||||||
|
<>
|
||||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||||
{t.name}
|
{t.name}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="w-1 h-full absolute top-0 left-0 bottom-0"
|
||||||
|
style={{ backgroundColor: t.color }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
itemKey={`${t.id}`}
|
itemKey={`${t.id}`}
|
||||||
>
|
>
|
||||||
|
@ -226,6 +226,7 @@ const en = {
|
|||||||
no_enums: "No enums",
|
no_enums: "No enums",
|
||||||
no_enums_text: "Define enums here",
|
no_enums_text: "Define enums here",
|
||||||
declare_array: "Declare array",
|
declare_array: "Declare array",
|
||||||
|
empty_index_name: "Declared an index with no name in table '{{tableName}}'",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,14 +176,14 @@ export function jsonToMySQL(obj) {
|
|||||||
: ""
|
: ""
|
||||||
}\n)${table.comment ? ` COMMENT='${table.comment}'` : ""};\n${
|
}\n)${table.comment ? ` COMMENT='${table.comment}'` : ""};\n${
|
||||||
table.indices.length > 0
|
table.indices.length > 0
|
||||||
? `\n${table.indices.map(
|
? `\n${table.indices
|
||||||
|
.map(
|
||||||
(i) =>
|
(i) =>
|
||||||
`\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${
|
`CREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${i.name}\`\nON \`${table.name}\` (${i.fields
|
||||||
i.name
|
|
||||||
}\`\nON \`${table.name}\` (${i.fields
|
|
||||||
.map((f) => `\`${f}\``)
|
.map((f) => `\`${f}\``)
|
||||||
.join(", ")});`,
|
.join(", ")});`,
|
||||||
)}`
|
)
|
||||||
|
.join("\n")}`
|
||||||
: ""
|
: ""
|
||||||
}`,
|
}`,
|
||||||
)
|
)
|
||||||
@ -251,10 +251,8 @@ export function jsonToPostgreSQL(obj) {
|
|||||||
field.name
|
field.name
|
||||||
}" ${getTypeString(field, obj.database, "postgres")}${
|
}" ${getTypeString(field, obj.database, "postgres")}${
|
||||||
field.notNull ? " NOT NULL" : ""
|
field.notNull ? " NOT NULL" : ""
|
||||||
}${
|
}${field.unique ? " UNIQUE" : ""}${
|
||||||
field.default !== ""
|
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""
|
||||||
? ` DEFAULT ${parseDefault(field, obj.database)}`
|
|
||||||
: ""
|
|
||||||
}${
|
}${
|
||||||
field.check === "" ||
|
field.check === "" ||
|
||||||
!dbToTypes[obj.database][field.type].hasCheck
|
!dbToTypes[obj.database][field.type].hasCheck
|
||||||
@ -271,14 +269,16 @@ export function jsonToPostgreSQL(obj) {
|
|||||||
: ""
|
: ""
|
||||||
}\n);\n${
|
}\n);\n${
|
||||||
table.indices.length > 0
|
table.indices.length > 0
|
||||||
? `${table.indices.map(
|
? `${table.indices
|
||||||
|
.map(
|
||||||
(i) =>
|
(i) =>
|
||||||
`\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${
|
`CREATE ${i.unique ? "UNIQUE " : ""}INDEX "${
|
||||||
i.name
|
i.name
|
||||||
}"\nON "${table.name}" (${i.fields
|
}"\nON "${table.name}" (${i.fields
|
||||||
.map((f) => `"${f}"`)
|
.map((f) => `"${f}"`)
|
||||||
.join(", ")});`,
|
.join(", ")});`,
|
||||||
)}`
|
)
|
||||||
|
.join("\n")}`
|
||||||
: ""
|
: ""
|
||||||
}`,
|
}`,
|
||||||
)
|
)
|
||||||
@ -426,14 +426,16 @@ export function jsonToMariaDB(obj) {
|
|||||||
: ""
|
: ""
|
||||||
}\n);${
|
}\n);${
|
||||||
table.indices.length > 0
|
table.indices.length > 0
|
||||||
? `\n${table.indices.map(
|
? `\n${table.indices
|
||||||
|
.map(
|
||||||
(i) =>
|
(i) =>
|
||||||
`\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${
|
`CREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${
|
||||||
i.name
|
i.name
|
||||||
}\`\nON \`${table.name}\` (${i.fields
|
}\`\nON \`${table.name}\` (${i.fields
|
||||||
.map((f) => `\`${f}\``)
|
.map((f) => `\`${f}\``)
|
||||||
.join(", ")});`,
|
.join(", ")});`,
|
||||||
)}`
|
)
|
||||||
|
.join("\n")}`
|
||||||
: ""
|
: ""
|
||||||
}`,
|
}`,
|
||||||
)
|
)
|
||||||
|
@ -99,6 +99,13 @@ export function getIssues(diagram) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
table.indices.forEach((index) => {
|
table.indices.forEach((index) => {
|
||||||
|
if (index.name.trim() === "") {
|
||||||
|
issues.push(
|
||||||
|
i18n.t("empty_index_name", {
|
||||||
|
tableName: table.name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (index.fields.length === 0) {
|
if (index.fields.length === 0) {
|
||||||
issues.push(
|
issues.push(
|
||||||
i18n.t("empty_index", {
|
i18n.t("empty_index", {
|
||||||
|
@ -7,7 +7,7 @@ export const getModalTitle = (modal) => {
|
|||||||
case MODAL.IMPORT_SRC:
|
case MODAL.IMPORT_SRC:
|
||||||
return i18n.t("import_diagram");
|
return i18n.t("import_diagram");
|
||||||
case MODAL.CODE:
|
case MODAL.CODE:
|
||||||
return i18n.t("export_source");
|
return i18n.t("export");
|
||||||
case MODAL.IMG:
|
case MODAL.IMG:
|
||||||
return i18n.t("export_image");
|
return i18n.t("export_image");
|
||||||
case MODAL.RENAME:
|
case MODAL.RENAME:
|
||||||
|
Loading…
Reference in New Issue
Block a user