query design ops, add reference

This commit is contained in:
Jan Prochazka
2020-12-30 08:33:39 +01:00
parent 4962d81661
commit 1de9b9f1fb
7 changed files with 186 additions and 19 deletions

View File

@@ -7,6 +7,7 @@ import useTheme from '../theme/useTheme';
import DesignerReference from './DesignerReference';
import cleanupDesignColumns from './cleanupDesignColumns';
import { isConnectedByReference } from './designerTools';
import { getTableInfo } from '../utility/metadataLoaders';
const Wrapper = styled.div`
flex: 1;
@@ -20,7 +21,7 @@ const Canvas = styled.div`
position: relative;
`;
export default function Designer({ value, onChange }) {
export default function Designer({ value, onChange, conid, database }) {
const { tables, references } = value || {};
const theme = useTheme();
@@ -39,10 +40,11 @@ export default function Designer({ value, onChange }) {
json.designerId = uuidv1();
json.left = e.clientX - rect.left;
json.top = e.clientY - rect.top;
onChange({
...value,
tables: [...(tables || []), json],
});
onChange((current) => ({
...current,
tables: [...(current.tables || []), json],
}));
};
const changeTable = React.useCallback(
@@ -70,6 +72,9 @@ export default function Designer({ value, onChange }) {
onChange((current) => ({
...current,
tables: (current.tables || []).filter((x) => x.designerId != table.designerId),
references: (current.references || []).filter(
(x) => x.sourceId != table.designerId && x.targetId != table.designerId
),
}));
},
[onChange]
@@ -144,6 +149,45 @@ export default function Designer({ value, onChange }) {
});
};
const handleAddReferenceByColumn = async (designerId, foreignKey) => {
const toTable = await getTableInfo({
conid,
database,
pureName: foreignKey.refTableName,
schemaName: foreignKey.refSchemaName,
});
const newTableDesignerId = uuidv1();
onChange((current) => {
const fromTable = (current.tables || []).find((x) => x.designerId == designerId);
if (!fromTable) return;
return {
...current,
tables: [
...(current.tables || []),
{
...toTable,
left: fromTable.left + 300,
top: fromTable.top + 50,
designerId: newTableDesignerId,
},
],
references: [
...(current.references || []),
{
designerId: uuidv1(),
sourceId: fromTable.designerId,
targetId: newTableDesignerId,
joinType: 'INNER JOIN',
columns: foreignKey.columns.map((col) => ({
source: col.columnName,
target: col.refColumnName,
})),
},
],
};
});
};
const handleSelectColumn = React.useCallback(
(column) => {
onChange((current) => ({
@@ -211,6 +255,7 @@ export default function Designer({ value, onChange }) {
onCreateReference={handleCreateReference}
onSelectColumn={handleSelectColumn}
onChangeColumn={handleChangeColumn}
onAddReferenceByColumn={handleAddReferenceByColumn}
table={table}
onChangeTable={changeTable}
onBringToFront={bringToFront}

View File

@@ -1,11 +1,16 @@
import React from 'react';
import styled from 'styled-components';
import { findForeignKeyForColumn } from 'dbgate-tools';
import ColumnLabel from '../datagrid/ColumnLabel';
import { FontIcon } from '../icons';
import useTheme from '../theme/useTheme';
import DomTableRef from './DomTableRef';
import _ from 'lodash';
import { CheckboxField } from '../utility/inputs';
import { useShowMenu } from '../modals/showMenu';
import { DropDownMenuDivider, DropDownMenuItem } from '../modals/DropDownMenu';
import useShowModal from '../modals/showModal';
import InputTextModal from '../modals/InputTextModal';
const Wrapper = styled.div`
position: absolute;
@@ -80,12 +85,50 @@ const ColumnLine = styled.div`
`}
`;
function TableContextMenu({ remove, setTableAlias, removeTableAlias }) {
return (
<>
<DropDownMenuItem onClick={remove}>Remove</DropDownMenuItem>
<DropDownMenuDivider />
<DropDownMenuItem onClick={setTableAlias}>Set table alias</DropDownMenuItem>
{!!removeTableAlias && <DropDownMenuItem onClick={removeTableAlias}>Remove table alias</DropDownMenuItem>}
</>
);
}
function ColumnContextMenu({ setSortOrder, addReference }) {
return (
<>
<DropDownMenuItem onClick={() => setSortOrder(1)}>Sort ascending</DropDownMenuItem>
<DropDownMenuItem onClick={() => setSortOrder(-1)}>Sort descending</DropDownMenuItem>
<DropDownMenuItem onClick={() => setSortOrder(0)}>Unsort</DropDownMenuItem>
{!!addReference && <DropDownMenuItem onClick={addReference}>Add reference</DropDownMenuItem>}
</>
);
}
function ColumnDesignerIcons({ column, designerId, designer }) {
const designerColumn = (designer.columns || []).find(
(x) => x.designerId == designerId && x.columnName == column.columnName
);
if (!designerColumn) return null;
return (
<>
{!!designerColumn.filter && <FontIcon icon="img filter" />}
{designerColumn.sortOrder > 0 && <FontIcon icon="img sort-asc" />}
{designerColumn.sortOrder < 0 && <FontIcon icon="img sort-desc" />}
{!!designerColumn.isGrouped && <FontIcon icon="img group" />}
</>
);
}
export default function DesignerTable({
table,
onChangeTable,
onBringToFront,
onRemoveTable,
onCreateReference,
onAddReferenceByColumn,
onSelectColumn,
onChangeColumn,
sourceDragColumn,
@@ -97,11 +140,13 @@ export default function DesignerTable({
setChangeToken,
designer,
}) {
const { pureName, columns, left, top, designerId } = table;
const { pureName, columns, left, top, designerId, alias } = table;
const [movingPosition, setMovingPosition] = React.useState(null);
const movingPositionRef = React.useRef(null);
const theme = useTheme();
const domObjectsRef = React.useRef({});
const showMenu = useShowMenu();
const showModal = useShowModal();
const moveStartXRef = React.useRef(null);
const moveStartYRef = React.useRef(null);
@@ -185,6 +230,71 @@ export default function DesignerTable({
changeTokenDebounced.current();
};
const handleSetTableAlias = () => {
showModal((modalState) => (
<InputTextModal
modalState={modalState}
value={alias || ''}
label="New alias"
header="Set table alias"
onConfirm={(newAlias) => {
onChangeTable({
...table,
alias: newAlias,
});
}}
/>
));
};
const handleHeaderContextMenu = (event) => {
event.preventDefault();
showMenu(
event.pageX,
event.pageY,
<TableContextMenu
remove={() => onRemoveTable({ designerId })}
setTableAlias={handleSetTableAlias}
removeTableAlias={
alias
? () =>
onChangeTable({
...table,
alias: null,
})
: null
}
/>
);
};
const handleColumnContextMenu = (column) => (event) => {
event.preventDefault();
const foreignKey = findForeignKeyForColumn(table, column);
showMenu(
event.pageX,
event.pageY,
<ColumnContextMenu
setSortOrder={(sortOrder) => {
onChangeColumn(
{
...column,
designerId,
},
(col) => ({ ...col, sortOrder })
);
}}
addReference={
foreignKey
? () => {
onAddReferenceByColumn(designerId, foreignKey);
}
: null
}
/>
);
};
return (
<Wrapper
theme={theme}
@@ -195,8 +305,8 @@ export default function DesignerTable({
onMouseDown={() => onBringToFront(table)}
ref={(dom) => dispatchDomColumn('', dom)}
>
<Header onMouseDown={headerMouseDown} theme={theme}>
<HeaderLabel>{pureName}</HeaderLabel>
<Header onMouseDown={headerMouseDown} theme={theme} onContextMenu={handleHeaderContextMenu}>
<HeaderLabel>{alias || pureName}</HeaderLabel>
<CloseWrapper onClick={() => onRemoveTable(table)} theme={theme}>
<FontIcon icon="icon close" />
</CloseWrapper>
@@ -204,6 +314,7 @@ export default function DesignerTable({
<ColumnsWrapper>
{(columns || []).map((column) => (
<ColumnLine
onContextMenu={handleColumnContextMenu(column)}
key={column.columnName}
theme={theme}
draggable
@@ -278,7 +389,8 @@ export default function DesignerTable({
}
}}
/>
<ColumnLabel {...column} forceIcon />
<ColumnLabel {...column} foreignKey={findForeignKeyForColumn(table, column)} forceIcon />
<ColumnDesignerIcons column={column} designerId={designerId} designer={designer} />
</ColumnLine>
))}
</ColumnsWrapper>

View File

@@ -3,5 +3,5 @@ import styled from 'styled-components';
import Designer from './Designer';
export default function QueryDesigner({ value, conid, database, engine, onChange }) {
return <Designer value={value} onChange={onChange}></Designer>;
return <Designer value={value} onChange={onChange} conid={conid} database={database}></Designer>;
}

View File

@@ -34,7 +34,7 @@ export function findConnectingReference(
tables2: DesignerTableInfo[],
additionalCondition: (ref: DesignerReferenceInfo) => boolean
) {
for (const ref of designer.references) {
for (const ref of designer.references || []) {
if (additionalCondition(ref) && referenceIsConnecting(ref, tables1, tables2)) {
return ref;
}
@@ -119,6 +119,7 @@ export function isConnectedByReference(
table2: { designerId: string },
withoutRef: { designerId: string }
) {
if (!designer.references) return false;
const creator = new DesignerComponentCreator({
...designer,
references: withoutRef

View File

@@ -85,6 +85,8 @@ const iconNames = {
'img reference': 'mdi mdi-link-box',
'img link': 'mdi mdi-link',
'img filter': 'mdi mdi-filter',
'img group': 'mdi mdi-group',
};
export function FontIcon({ icon, className = '', ...other }) {

View File

@@ -102,7 +102,7 @@ export default function QueryDesignTab({
sesid,
sql: sqlPreview,
});
}, [busy]);
}, [busy, conid, sessionId, database, sqlPreview]);
const handleCancel = () => {
axios.post('sessions/cancel', {
@@ -118,12 +118,15 @@ export default function QueryDesignTab({
setBusy(false);
};
const handleKeyDown = React.useCallback((e) => {
if (e.keyCode == keycodes.f5) {
e.preventDefault();
handleExecute();
}
}, []);
const handleKeyDown = React.useCallback(
(e) => {
if (e.keyCode == keycodes.f5) {
e.preventDefault();
handleExecute();
}
},
[handleExecute]
);
React.useEffect(() => {
if (tabVisible) {