Skip to content

Commit 96543b9

Browse files
authored
Fixing schema designer filtering and auto layout behavior. (#19475)
1 parent fa6ce66 commit 96543b9

File tree

7 files changed

+264
-151
lines changed

7 files changed

+264
-151
lines changed

localization/l10n/bundle.l10n.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,12 @@
360360
"Auto Arrange Confirmation": "Auto Arrange Confirmation",
361361
"Auto Arrange will automatically reposition all diagram elements based on optimal layout algorithms. Any custom positioning you've created will be lost. Do you want to proceed with auto-arranging your schema diagram?": "Auto Arrange will automatically reposition all diagram elements based on optimal layout algorithms. Any custom positioning you've created will be lost. Do you want to proceed with auto-arranging your schema diagram?",
362362
"Filter": "Filter",
363+
"Filter ({0})/{0} is the number of selected tables": {
364+
"message": "Filter ({0})",
365+
"comment": [
366+
"{0} is the number of selected tables"
367+
]
368+
},
363369
"Refresh": "Refresh",
364370
"Publish Changes": "Publish Changes",
365371
"Edit Table": "Edit Table",
@@ -532,6 +538,7 @@
532538
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
533539
"Undo": "Undo",
534540
"Redo": "Redo",
541+
"Search tables...": "Search tables...",
535542
"To compare two schemas, first select a source schema and target schema, then press compare.": "To compare two schemas, first select a source schema and target schema, then press compare.",
536543
"No schema differences were found.": "No schema differences were found.",
537544
"Initializing comparison, this might take a while...": "Initializing comparison, this might take a while...",

localization/xliff/vscode-mssql.xlf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,10 @@
850850
<trans-unit id="++CODE++638e249f4a15ebb84957da130701d138a6e06d88dceeaa2e6dcd91db70cc1381">
851851
<source xml:lang="en">Filter</source>
852852
</trans-unit>
853+
<trans-unit id="++CODE++a76057f27f2880792cf681fd60ca7b9043463252597c22b60eb5beca29292017">
854+
<source xml:lang="en">Filter ({0})</source>
855+
<note>{0} is the number of selected tables</note>
856+
</trans-unit>
853857
<trans-unit id="++CODE++3905257a1ca17a05ee44a95330afe24483f05af738cc05090d1c2522910a5671">
854858
<source xml:lang="en">Filter Azure subscriptions</source>
855859
</trans-unit>
@@ -1643,6 +1647,9 @@
16431647
<trans-unit id="++CODE++6801207d5994165123a10ab767fd9439130fae500f8c407877ae5070843b906d">
16441648
<source xml:lang="en">Search settings...</source>
16451649
</trans-unit>
1650+
<trans-unit id="++CODE++771f5d2c0599dcf4194e3fdddd9a5212d6392f4703d29971575bb455b5287a84">
1651+
<source xml:lang="en">Search tables...</source>
1652+
</trans-unit>
16461653
<trans-unit id="++CODE++7f55382219f0202c1b4f56deb099e2fedcec87ee73fe2edb2105df5447323bc6">
16471654
<source xml:lang="en">Search...</source>
16481655
</trans-unit>

src/reactviews/common/locConstants.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,17 @@ export class LocConstants {
386386
autoArrangeConfirmationContent: l10n.t(
387387
"Auto Arrange will automatically reposition all diagram elements based on optimal layout algorithms. Any custom positioning you've created will be lost. Do you want to proceed with auto-arranging your schema diagram?",
388388
),
389-
filter: l10n.t("Filter"),
389+
filter: (selectedTablesCount: number) => {
390+
if (selectedTablesCount === 0) {
391+
return l10n.t("Filter");
392+
} else {
393+
return l10n.t({
394+
message: "Filter ({0})",
395+
args: [selectedTablesCount],
396+
comment: ["{0} is the number of selected tables"],
397+
});
398+
}
399+
},
390400
clearFilter: l10n.t("Clear All"),
391401
applyFilter: l10n.t("Apply"),
392402
refresh: l10n.t("Refresh"),
@@ -586,6 +596,7 @@ export class LocConstants {
586596
),
587597
undo: l10n.t("Undo"),
588598
redo: l10n.t("Redo"),
599+
searchTables: l10n.t("Search tables..."),
589600
};
590601
}
591602

src/reactviews/pages/SchemaDesigner/schemaDesignerStateProvider.tsx

Lines changed: 77 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useVscodeWebview } from "../../common/vscodeWebviewProvider";
99
import { getCoreRPCs } from "../../common/utils";
1010
import { WebviewRpc } from "../../common/rpc";
1111

12-
import { Edge, Node, ReactFlowJsonObject, useReactFlow } from "@xyflow/react";
12+
import { Edge, MarkerType, Node, ReactFlowJsonObject, useReactFlow } from "@xyflow/react";
1313
import { flowUtils, foreignKeyUtils } from "./schemaDesignerUtils";
1414
import eventBus from "./schemaDesignerEvents";
1515
import { UndoRedoStack } from "../../common/undoRedoStack";
@@ -48,6 +48,7 @@ export interface SchemaDesignerContextProps
4848
}>;
4949
closeDesigner: () => void;
5050
resetUndoRedoState: () => void;
51+
resetView: () => void;
5152
isInitialized: boolean;
5253
}
5354

@@ -215,45 +216,30 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
215216
* Adds a new table to the flow
216217
*/
217218
const addTable = async (table: SchemaDesigner.Table) => {
218-
const newReactFlowNode: Node<SchemaDesigner.Table> = {
219-
id: table.id,
220-
type: "tableNode",
221-
data: { ...table },
222-
position: { x: 0, y: 0 },
223-
};
219+
const existingNodes = reactFlow.getNodes() as Node<SchemaDesigner.Table>[];
220+
const existingEdges = reactFlow.getEdges() as Edge<SchemaDesigner.ForeignKey>[];
224221

225-
const schemaModel = flowUtils.extractSchemaModel(
226-
reactFlow.getNodes() as Node<SchemaDesigner.Table>[],
227-
reactFlow.getEdges() as Edge<SchemaDesigner.ForeignKey>[],
228-
);
222+
const schemaModel = flowUtils.extractSchemaModel(existingNodes, existingEdges);
223+
224+
schemaModel.tables.push(table);
229225

230-
schemaModel.tables.push(newReactFlowNode.data);
231226
const updatedPositions = flowUtils.generateSchemaDesignerFlowComponents(schemaModel);
232227

233-
const nodeWithPosition = updatedPositions.nodes.find(
234-
(node) => node.id === newReactFlowNode.id,
235-
);
228+
const nodeWithPosition = updatedPositions.nodes.find((node) => node.id === table.id);
236229

237230
const edgesForNewTable = updatedPositions.edges.filter(
238-
(edge) => edge.source === newReactFlowNode.id || edge.target === newReactFlowNode.id,
231+
(edge) => edge.source === table.id || edge.target === table.id,
239232
);
240233

241234
if (nodeWithPosition) {
242-
nodeWithPosition.selected = true;
243-
reactFlow.addNodes(nodeWithPosition);
244-
reactFlow.addEdges(edgesForNewTable);
245-
// update all affected positions
246-
for (const updatedNode of updatedPositions.nodes) {
247-
if (updatedNode.id === nodeWithPosition.id) continue;
248-
reactFlow.updateNode(updatedNode.id, {
249-
position: updatedNode.position,
250-
data: updatedNode.data,
251-
});
252-
}
235+
existingNodes.push(nodeWithPosition);
236+
existingEdges.push(...edgesForNewTable);
237+
238+
reactFlow.setNodes(existingNodes);
239+
reactFlow.setEdges(existingEdges);
253240
requestAnimationFrame(async () => {
254241
setCenter(nodeWithPosition.id, true);
255242
});
256-
257243
eventBus.emit("getScript");
258244
return true;
259245
}
@@ -264,68 +250,71 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
264250
/**
265251
* Updates a table in the flow
266252
*/
267-
const updateTable = async (table: SchemaDesigner.Table) => {
268-
const schemaModel = flowUtils.extractSchemaModel(
269-
reactFlow.getNodes() as Node<SchemaDesigner.Table>[],
270-
reactFlow.getEdges() as Edge<SchemaDesigner.ForeignKey>[],
271-
);
253+
const updateTable = async (updatedTable: SchemaDesigner.Table) => {
254+
const existingNodes = reactFlow.getNodes() as Node<SchemaDesigner.Table>[];
255+
let existingEdges = reactFlow.getEdges() as Edge<SchemaDesigner.ForeignKey>[];
256+
257+
const existingTableNode = existingNodes.find((node) => node.id === updatedTable.id);
272258

273-
const tableNode = schemaModel.tables.find((node) => node.id === table.id);
274-
if (!tableNode) {
275-
console.warn(`Table with id ${table.id} not found`);
259+
if (!existingTableNode) {
276260
return false;
277261
}
278262

279-
for (const t of schemaModel.tables) {
280-
if (t.id !== table.id) {
281-
// Update the foreign keys for other tables
282-
t.foreignKeys.forEach((fk) => {
283-
if (
284-
fk.referencedSchemaName === tableNode.schema &&
285-
fk.referencedTableName === tableNode.name
286-
) {
287-
fk.referencedSchemaName = table.schema;
288-
fk.referencedTableName = table.name;
289-
}
290-
});
263+
// Updating the table name and schema in all foreign keys that reference this table
264+
// This is necessary because the table name and schema might have changed
265+
existingEdges.forEach((edge) => {
266+
if (
267+
edge?.data?.referencedSchemaName === existingTableNode?.data?.schema &&
268+
edge?.data?.referencedTableName === existingTableNode?.data?.name
269+
) {
270+
edge.data.referencedSchemaName = updatedTable.schema;
271+
edge.data.referencedTableName = updatedTable.name;
291272
}
292-
}
293-
294-
const updatedTable = {
295-
...tableNode,
296-
...table,
297-
};
298-
299-
const updatedSchema = {
300-
...schemaModel,
301-
tables: schemaModel.tables.map((node) => (node.id === table.id ? updatedTable : node)),
302-
};
303-
304-
// Delete existing edges for this table
305-
const edgesToDelete = reactFlow
306-
.getEdges()
307-
.filter((edge) => edge.source === table.id || edge.target === table.id);
308-
309-
await reactFlow.deleteElements({
310-
nodes: [],
311-
edges: edgesToDelete,
312273
});
313274

314-
// Regenerate flow components with updated schema
315-
const newFlowComponents = flowUtils.generateSchemaDesignerFlowComponents(updatedSchema);
275+
// Update the table node with the new data
276+
existingTableNode.data = updatedTable;
316277

317-
const nodeWithPosition = newFlowComponents.nodes.find((node) => node.id === table.id);
278+
// Remove the existing foreign keys from the table
279+
existingEdges = existingEdges.filter((edge) => edge.source !== updatedTable.id);
318280

319-
if (nodeWithPosition) {
320-
const edgesForUpdatedTable = newFlowComponents.edges.filter(
321-
(edge) => edge.source === table.id || edge.target === table.id,
281+
// Add the new foreign keys to the table
282+
updatedTable.foreignKeys.forEach((foreignKey) => {
283+
const referencedTable = existingNodes.find(
284+
(node) =>
285+
node.data.schema === foreignKey.referencedSchemaName &&
286+
node.data.name === foreignKey.referencedTableName,
322287
);
288+
if (!referencedTable) {
289+
return;
290+
}
323291

324-
reactFlow.updateNodeData(nodeWithPosition.id, nodeWithPosition.data);
325-
reactFlow.addEdges(edgesForUpdatedTable);
326-
return true;
327-
}
328-
return false;
292+
foreignKey.columns.forEach((column, index) => {
293+
const referencedColumn = foreignKey.referencedColumns[index];
294+
existingEdges.push({
295+
id: foreignKey.id,
296+
source: updatedTable.id,
297+
target: referencedTable.id,
298+
sourceHandle: `right-${column}`,
299+
targetHandle: `left-${referencedColumn}`,
300+
markerEnd: {
301+
type: MarkerType.ArrowClosed,
302+
},
303+
data: {
304+
...foreignKey,
305+
referencedColumns: [referencedColumn],
306+
columns: [column],
307+
},
308+
});
309+
});
310+
});
311+
312+
reactFlow.setNodes(existingNodes);
313+
reactFlow.setEdges(existingEdges);
314+
requestAnimationFrame(() => {
315+
setCenter(updatedTable.id, true);
316+
});
317+
return true;
329318
};
330319

331320
const deleteTable = async (table: SchemaDesigner.Table) => {
@@ -409,6 +398,14 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
409398
eventBus.emit("updateUndoRedoState", stateStack.canUndo(), stateStack.canRedo());
410399
};
411400

401+
function resetView() {
402+
setTimeout(async () => {
403+
await reactFlow.fitView({
404+
nodes: reactFlow.getNodes().filter((node) => node.hidden !== true),
405+
});
406+
}, 10);
407+
}
408+
412409
return (
413410
<SchemaDesignerContext.Provider
414411
value={{
@@ -437,6 +434,7 @@ const SchemaDesignerStateProvider: React.FC<SchemaDesignerProviderProps> = ({ ch
437434
isInitialized,
438435
closeDesigner,
439436
resetUndoRedoState,
437+
resetView,
440438
}}>
441439
{children}
442440
</SchemaDesignerContext.Provider>

src/reactviews/pages/SchemaDesigner/schemaDesignerUtils.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,53 @@ export const flowUtils = {
771771
getTableHeight: (table: SchemaDesigner.Table): number =>
772772
LAYOUT_CONSTANTS.BASE_NODE_HEIGHT + table.columns.length * LAYOUT_CONSTANTS.COLUMN_HEIGHT,
773773

774+
generatePositions: (
775+
nodes: Node<SchemaDesigner.Table>[],
776+
edges: Edge<SchemaDesigner.ForeignKey>[],
777+
): {
778+
nodes: Node<SchemaDesigner.Table>[];
779+
edges: Edge<SchemaDesigner.ForeignKey>[];
780+
} => {
781+
const graph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
782+
graph.setGraph(LAYOUT_CONSTANTS.LAYOUT_OPTIONS);
783+
784+
for (const node of nodes) {
785+
if (node.hidden) continue;
786+
graph.setNode(node.id, {
787+
width: flowUtils.getTableWidth(),
788+
height: flowUtils.getTableHeight(node.data),
789+
});
790+
}
791+
792+
for (const edge of edges) {
793+
if (edge.hidden) continue;
794+
const sourceNode = nodes.find((n) => n.id === edge.source);
795+
const targetNode = nodes.find((n) => n.id === edge.target);
796+
if (!sourceNode?.hidden && !targetNode?.hidden) {
797+
graph.setEdge(edge.source, edge.target);
798+
}
799+
}
800+
801+
dagre.layout(graph);
802+
803+
const layoutedNodes = nodes.map((node) => {
804+
if (node.hidden) return node;
805+
const dagreNode = graph.node(node.id);
806+
return {
807+
...node,
808+
position: {
809+
x: dagreNode.x - flowUtils.getTableWidth() / 2,
810+
y: dagreNode.y - flowUtils.getTableHeight(node.data) / 2,
811+
},
812+
};
813+
});
814+
815+
return {
816+
nodes: layoutedNodes,
817+
edges,
818+
};
819+
},
820+
774821
generateSchemaDesignerFlowComponents: (
775822
schema: SchemaDesigner.Schema,
776823
): {

src/reactviews/pages/SchemaDesigner/toolbar/autoArrangeButton.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ export function AutoArrangeButton() {
2929
const autoArrange = () => {
3030
eventBus.emit("pushState");
3131
const nodes = reactFlow.getNodes() as Node<SchemaDesigner.Table>[];
32-
const schema = flowUtils.extractSchemaModel(
33-
nodes,
34-
reactFlow.getEdges() as Edge<SchemaDesigner.ForeignKey>[],
35-
);
36-
const generateComponenets = flowUtils.generateSchemaDesignerFlowComponents(schema);
32+
const edges = reactFlow.getEdges() as Edge<SchemaDesigner.ForeignKey>[];
33+
const generateComponenets = flowUtils.generatePositions(nodes, edges);
3734
reactFlow.setNodes(generateComponenets.nodes);
3835
reactFlow.setEdges(generateComponenets.edges);
36+
context.resetView();
3937
};
4038
if (!context) {
4139
return undefined;

0 commit comments

Comments
 (0)