Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 59 additions & 26 deletions src/components/EditorCanvas/Table.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import {
Tab,
ObjectType,
Expand All @@ -22,7 +22,7 @@ import i18n from "../../i18n/i18n";

export default function Table(props) {
const [hoveredField, setHoveredField] = useState(-1);
const { database } = useDiagram();
const { database, updateTable } = useDiagram();
const {
tableData,
onPointerDown,
Expand All @@ -37,6 +37,39 @@ export default function Table(props) {
const { t } = useTranslation();
const { selectedElement, setSelectedElement } = useSelect();

useEffect(() => {
// Check if we need to update the table name
const desiredTableCase = settings.upperCaseFields ? tableData.name.toUpperCase() : tableData.name.toLowerCase();
const tableNameNeedsUpdate = tableData.name !== desiredTableCase;

// Check if any field names need to be updated
const fieldsNeedUpdate = tableData.fields.some(field => {
const desiredFieldCase = settings.upperCaseFields ? field.name.toUpperCase() : field.name.toLowerCase();
return field.name !== desiredFieldCase;
});

// Only update if there are actual changes needed
if (tableNameNeedsUpdate || fieldsNeedUpdate) {
// Create updated fields with correct case
const updatedFields = tableData.fields.map(field => ({
...field,
name: settings.upperCaseFields ? field.name.toUpperCase() : field.name.toLowerCase()
}));

// Update both table name and fields
updateTable(tableData.id, {
name: settings.upperCaseFields ? tableData.name.toUpperCase() : tableData.name.toLowerCase(),
fields: updatedFields
});
}
}, [
settings.upperCaseFields,
tableData.fields,
tableData.id,
tableData.name,
updateTable
]);

const height =
tableData.fields.length * tableFieldHeight + tableHeaderHeight + 7;
const openEditor = () => {
Expand Down Expand Up @@ -358,37 +391,37 @@ export default function Table(props) {
/>
) : (
<div className="flex gap-1 items-center">
{fieldData.primary &&
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="#ff2222cc"
className="bi bi-key"
{fieldData.primary &&
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="#ff2222cc"
className="bi bi-key"
viewBox="0 0 16 16"
>
<path d="M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0
.708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11
9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4
4 0 0 1 0 8m4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5
0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1
<path d="M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0
.708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11
9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4
4 0 0 1 0 8m4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5
0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1
.708 0l.646.647.793-.793-1-1h-6.63a.5.5 0 0 1-.451-.285A3 3 0 0 0 4 5"/>
<path d="M4 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/>
</svg>}
{fieldData.foreignK &&
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="#2f68adcc"
className="bi bi-key"
{fieldData.foreignK &&
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="#2f68adcc"
className="bi bi-key"
viewBox="0 0 16 16"
>
<path d="M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0
.708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11
9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4
4 0 0 1 0 8m4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5
0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1
<path d="M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0
.708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11
9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4
4 0 0 1 0 8m4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5
0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1
.708 0l.646.647.793-.793-1-1h-6.63a.5.5 0 0 1-.451-.285A3 3 0 0 0 4 5"/>
<path d="M4 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/>
</svg>}
Expand Down
3 changes: 3 additions & 0 deletions src/components/EditorHeader/ControlPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,9 @@ export default function ControlPanel({
table_width: {
function: () => setModal(MODAL.TABLE_WIDTH),
},
defaults : {
function: () => setModal(MODAL.DEFAULTS),
},
language: {
function: () => setModal(MODAL.LANGUAGE),
},
Expand Down
153 changes: 153 additions & 0 deletions src/components/EditorHeader/Modal/Defaults.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useState, useEffect } from "react";
import { Form, Input, Button, Toast, Divider } from "@douyinfe/semi-ui";
import { useSettings, useDiagram } from "../../../hooks";
import { useTranslation } from "react-i18next";
import { dbToTypes } from "../../../data/datatypes";

export default function Defaults() {
const { t } = useTranslation();
const { settings, setSettings } = useSettings();
const { database } = useDiagram();


const [editingFKNamingTemplate, setEditingFKNamingTemplate] = useState(settings.fkConstraintNaming?.template || "");
const [editingIndexNamingTemplate, setEditingIndexNamingTemplate] = useState(settings.indexNaming?.template || "");

useEffect(() => {
if (settings) {
if (settings.fkConstraintNaming?.template) {
setEditingFKNamingTemplate(settings.fkConstraintNaming.template);
}
if (settings.indexNaming?.template) {
setEditingIndexNamingTemplate(settings.indexNaming.template);
}
}
}, [settings]);

const handleChange = (field, value) => {
setSettings(prev => ({
...prev, [field]: value
}));
};

const handleSaveNamingConventions = () => {
setSettings(prevSettings => ({
...prevSettings,
fkConstraintNaming: {
...prevSettings.fkConstraintNaming,
template: editingFKNamingTemplate,
},
indexNaming: {
...prevSettings.indexNaming,
template: editingIndexNamingTemplate,
},
}));
Toast.success(t("naming_conventions_saved_successfully"));
};

const previewFKName = (template) => {
const exampleTable1 = "orders";
const exampleField1 = "id";
const exampleTable2 = "customers";
const exampleField2 = "customer_id";
let preview = template;
preview = preview.replace(/{table1}/g, exampleTable1);
preview = preview.replace(/{field1}/g, exampleField1);
preview = preview.replace(/{table2}/g, exampleTable2);
preview = preview.replace(/{field2}/g, exampleField2);
return preview;
};

const previewIndexName = (template) => {
const exampleTable = "products";
const exampleIndexType = "unique";
const exampleFields = "name,code";
let preview = template;
preview = preview.replace(/{table}/g, exampleTable);
preview = preview.replace(/{indexType}/g, exampleIndexType);
preview = preview.replace(/{fields}/g, exampleFields);
return preview;
};

return (
<div className="w-[450px] p-4">
<Form initValues={{
defaultFieldType: settings.defaultFieldType,
upperCaseFields: settings.upperCaseFields,
defaultNotNull: settings.defaultNotNull
}}>
<Form.Section text={t("field_defaults")}>
<Form.Select
field="defaultFieldType"
label={t("default_field_type")}
defaultValue={settings.defaultFieldType}
onChange={(value) => handleChange('defaultFieldType', value)}
>
{Object.keys(dbToTypes[database]).map(type => (
<Form.Select.Option key={type} value={type}>
{type}
</Form.Select.Option>
))}
</Form.Select>

<Form.Switch
field="upperCaseFields"
label={t("uppercase_fields")}
defaultChecked={settings.upperCaseFields}
onChange={(checked) => handleChange('upperCaseFields', checked)}
/>

<Form.Switch
field="defaultNotNull"
label={t("default_not_null")}
defaultChecked={settings.defaultNotNull}
onChange={(checked) => handleChange('defaultNotNull', checked)}
/>
</Form.Section>
</Form>

<Divider />
<h3 className="text-lg font-semibold mb-3 mt-4">{t("naming_conventions")}</h3>

<div className="mb-4">
<label className="block text-sm font-medium mb-1">{t("foreign_key")}:</label>
<Input
value={editingFKNamingTemplate}
onChange={(value) => setEditingFKNamingTemplate(value)}
placeholder="{table1}_{table2}_{field1}_fk"
title={t("fk_template_info")}
style={{ width: '100%' }}
/>
<p className="text-xs text-gray-500 mt-1">
{t("fk_variables_available")} `&#123;table1&#125;`, `&#123;table2&#125;`, `&#123;field1&#125;`, `&#123;field2&#125;`
</p>
<p className="text-xs text-gray-500 mt-1">
{t("preview")}: {previewFKName(editingFKNamingTemplate)}
</p>
</div>

<div className="mb-4">
<label className="block text-sm font-medium mb-1">{t("index_naming")}:</label>
<Input
value={editingIndexNamingTemplate}
onChange={(value) => setEditingIndexNamingTemplate(value)}
placeholder="{table}_{indexType}_{fields}_idx"
title={t("index_template_info")}
style={{ width: '100%' }}
/>
<p className="text-xs text-gray-500 mt-1">
{t("index_template_variables_available")} `&#123;table&#125;`, `&#123;indexType&#125;` (e.g., unique), `&#123;fields&#125;` (comma-separated)
</p>
<p className="text-xs text-gray-500 mt-1">
{t("preview")}: {previewIndexName(editingIndexNamingTemplate)}
</p>
</div>

<div className="flex justify-end gap-2 mt-4">
<Button onClick={handleSaveNamingConventions} type="primary">
{t("save_foreign_key")}
</Button>
</div>
</div>
);
}
3 changes: 3 additions & 0 deletions src/components/EditorHeader/Modal/Modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import SetTableWidth from "./SetTableWidth";
import Language from "./Language";
import Share from "./Share";
import Code from "./Code";
import Defaults from "./Defaults";
import { useTranslation } from "react-i18next";
import { importSQL } from "../../../utils/importSQL";
import { databases } from "../../../data/databases";
Expand Down Expand Up @@ -315,6 +316,8 @@ export default function Modal({
}
case MODAL.TABLE_WIDTH:
return <SetTableWidth />;
case MODAL.DEFAULTS:
return <Defaults />
case MODAL.LANGUAGE:
return <Language />;
case MODAL.SHARE:
Expand Down
Loading