From b89a63553f529f3702d0d95bfcd4e82125258bbb Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Mon, 9 Sep 2024 16:23:47 +0300 Subject: [PATCH] redo/undo fix --- src/components/alert-dialog/alert-dialog.tsx | 19 ++-- src/components/toast/toast.tsx | 2 +- .../chartdb-context/chartdb-provider.tsx | 5 +- src/i18n/locales/en.ts | 8 -- src/lib/domain/db-relationship.ts | 15 +++ src/lib/domain/db-table.ts | 34 +++--- src/lib/utils.ts | 2 + src/pages/editor-page/canvas/canvas.tsx | 103 +++++------------- src/pages/editor-page/canvas/table-node.tsx | 65 +++-------- .../relationships-section.tsx | 11 +- .../tables-section/tables-section.tsx | 6 +- 11 files changed, 102 insertions(+), 168 deletions(-) diff --git a/src/components/alert-dialog/alert-dialog.tsx b/src/components/alert-dialog/alert-dialog.tsx index 397beb8e..3d0b3156 100644 --- a/src/components/alert-dialog/alert-dialog.tsx +++ b/src/components/alert-dialog/alert-dialog.tsx @@ -29,14 +29,17 @@ const AlertDialogContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + + + + )); AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; diff --git a/src/components/toast/toast.tsx b/src/components/toast/toast.tsx index 4fa23f2b..9d359642 100644 --- a/src/components/toast/toast.tsx +++ b/src/components/toast/toast.tsx @@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef< = ({ ); }; - const prevTables = [...tables]; - // const updatedTablesAttrs = updateFn(tables); + const prevTables = deepCopy(tables); const updatedTables = updateTables(tables); setTables(updateTables); diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index b269bab5..4c8f8a28 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -223,14 +223,6 @@ export const en = { one_to_many: 'One to Many', many_to_one: 'Many to One', }, - - toast: { - reorder: { - title: 'Tables reordered', - description: 'Click undo to revert changes', - undo: 'Undo', - }, - }, }, }; diff --git a/src/lib/domain/db-relationship.ts b/src/lib/domain/db-relationship.ts index 95269f0f..1ea61692 100644 --- a/src/lib/domain/db-relationship.ts +++ b/src/lib/domain/db-relationship.ts @@ -1,4 +1,5 @@ import { ForeignKeyInfo } from '../data/import-metadata/metadata-types/foreign-key-info'; +import { schemaNameToSchemaId } from './db-schema'; import { DBTable } from './db-table'; import { generateId } from '@/lib/utils'; @@ -17,6 +18,20 @@ export interface DBRelationship { export type RelationshipType = 'one_to_one' | 'one_to_many' | 'many_to_one'; +export const shouldShowRelationshipBySchemaFilter = ( + relationship: DBRelationship, + filteredSchemas?: string[] +): boolean => + !filteredSchemas || + !relationship.sourceSchema || + !relationship.targetSchema || + (filteredSchemas.includes( + schemaNameToSchemaId(relationship.sourceSchema) + ) && + filteredSchemas.includes( + schemaNameToSchemaId(relationship.targetSchema) + )); + export const createRelationshipsFromMetadata = ({ foreignKeys, tables, diff --git a/src/lib/domain/db-table.ts b/src/lib/domain/db-table.ts index d50869f9..a970b493 100644 --- a/src/lib/domain/db-table.ts +++ b/src/lib/domain/db-table.ts @@ -7,7 +7,8 @@ import { greyColor, randomColor } from '@/lib/colors'; import { DBRelationship } from './db-relationship'; import { PrimaryKeyInfo } from '../data/import-metadata/metadata-types/primary-key-info'; import { ViewInfo } from '../data/import-metadata/metadata-types/view-info'; -import { generateId } from '../utils'; +import { deepCopy, generateId } from '../utils'; +import { schemaNameToSchemaId } from './db-schema'; export interface DBTable { id: string; @@ -25,6 +26,14 @@ export interface DBTable { hidden?: boolean; } +export const shouldShowTablesBySchemaFilter = ( + table: DBTable, + filteredSchemas?: string[] +): boolean => + !filteredSchemas || + !table.schema || + filteredSchemas.includes(schemaNameToSchemaId(table.schema)); + export const createTablesFromMetadata = ({ tableInfos, columns, @@ -139,25 +148,20 @@ export const createTablesFromMetadata = ({ }; export const adjustTablePositions = ({ - relationships, - tables, - filteredSchemas, + relationships: inputRelationships, + tables: inputTables, }: { tables: DBTable[]; relationships: DBRelationship[]; - filteredSchemas?: string[]; }): DBTable[] => { - const filteredTables = filteredSchemas - ? tables.filter( - (table) => !table.schema || filteredSchemas.includes(table.schema) - ) - : tables; + const tables = deepCopy(inputTables); + const relationships = deepCopy(inputRelationships); // Filter relationships to only include those between filtered tables const filteredRelationships = relationships.filter( (rel) => - filteredTables.some((t) => t.id === rel.sourceTableId) && - filteredTables.some((t) => t.id === rel.targetTableId) + tables.some((t) => t.id === rel.sourceTableId) && + tables.some((t) => t.id === rel.targetTableId) ); const tableWidth = 200; @@ -181,7 +185,7 @@ export const adjustTablePositions = ({ }); // Sort tables by number of connections - const sortedTables = [...filteredTables].sort( + const sortedTables = [...tables].sort( (a, b) => (tableConnections.get(b.id)?.size || 0) - (tableConnections.get(a.id)?.size || 0) @@ -256,7 +260,7 @@ export const adjustTablePositions = ({ connectedTables.forEach((connectedTableId) => { if (!positionedTables.has(connectedTableId)) { - const connectedTable = filteredTables.find( + const connectedTable = tables.find( (t) => t.id === connectedTableId ); if (connectedTable) { @@ -281,7 +285,7 @@ export const adjustTablePositions = ({ }); // Apply positions to filtered tables - filteredTables.forEach((table) => { + tables.forEach((table) => { const position = tablePositions.get(table.id); if (position) { table.x = position.x; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 1574d858..16a41304 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -24,3 +24,5 @@ export const getOperatingSystem = (): 'mac' | 'windows' | 'unknown' => { } return 'unknown'; }; + +export const deepCopy = (obj: T): T => JSON.parse(JSON.stringify(obj)); diff --git a/src/pages/editor-page/canvas/canvas.tsx b/src/pages/editor-page/canvas/canvas.tsx index 7e7e02fa..af57db00 100644 --- a/src/pages/editor-page/canvas/canvas.tsx +++ b/src/pages/editor-page/canvas/canvas.tsx @@ -30,9 +30,12 @@ import { useBreakpoint } from '@/hooks/use-breakpoint'; import { Badge } from '@/components/badge/badge'; import { useTheme } from '@/hooks/use-theme'; import { useTranslation } from 'react-i18next'; -import { DBTable, adjustTablePositions } from '@/lib/domain/db-table'; +import { + DBTable, + adjustTablePositions, + shouldShowTablesBySchemaFilter, +} from '@/lib/domain/db-table'; import { useLocalConfig } from '@/hooks/use-local-config'; -import { schemaNameToSchemaId } from '@/lib/domain/db-schema'; import { Tooltip, TooltipTrigger, @@ -55,10 +58,7 @@ const tableToTableNode = ( table, }, width: table.width ?? MIN_TABLE_SIZE, - hidden: - !!table.schema && - !!filteredSchemas && - !filteredSchemas.includes(schemaNameToSchemaId(table.schema)), + hidden: !shouldShowTablesBySchemaFilter(table, filteredSchemas), }); export interface CanvasProps { @@ -285,80 +285,35 @@ export const Canvas: React.FC = ({ initialTables }) => { const isLoadingDOM = tables.length > 0 ? !getInternalNode(tables[0].id) : false; + const reorderTables = useCallback(() => { + const newTables = adjustTablePositions({ + relationships, + tables: tables.filter((table) => + shouldShowTablesBySchemaFilter(table, filteredSchemas) + ), + }); + + updateTablesState((currentTables) => + currentTables.map((table) => { + const newTable = newTables.find((t) => t.id === table.id); + return { + id: table.id, + x: newTable?.x ?? table.x, + y: newTable?.y ?? table.y, + }; + }) + ); + }, [filteredSchemas, relationships, tables, updateTablesState]); + const showReorderConfirmation = useCallback(() => { showAlert({ title: t('reorder_diagram_alert.title'), description: t('reorder_diagram_alert.description'), actionLabel: t('reorder_diagram_alert.reorder'), closeLabel: t('reorder_diagram_alert.cancel'), - onAction: () => { - const originalTables = tables.map((table) => ({ ...table })); - const newTables = adjustTablePositions({ - relationships, - tables, - filteredSchemas, - }); - updateTablesState(() => newTables, { - updateHistory: false, - forceOverride: true, - }); - setNodes( - newTables.map((table) => - tableToTableNode(table, filteredSchemas) - ) - ); - fitView({ padding: 0.2, duration: 1000 }); - - const { dismiss } = toast({ - title: t('toast.reorder.title'), - description: t('toast.reorder.description'), - duration: 15000, - action: ( - - ), - }); - }, + onAction: reorderTables, }); - }, [ - showAlert, - t, - relationships, - tables, - updateTablesState, - setNodes, - fitView, - toast, - filteredSchemas, - ]); - - const reorderTables = useCallback(() => { - showReorderConfirmation(); - }, [showReorderConfirmation]); + }, [t, showAlert, reorderTables]); return (
@@ -397,7 +352,7 @@ export const Canvas: React.FC = ({ initialTables }) => { diff --git a/src/pages/editor-page/canvas/table-node.tsx b/src/pages/editor-page/canvas/table-node.tsx index 57a7b42c..a808dc8e 100644 --- a/src/pages/editor-page/canvas/table-node.tsx +++ b/src/pages/editor-page/canvas/table-node.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback } from 'react'; import { NodeProps, Node, NodeResizer, useStore } from '@xyflow/react'; import { Button } from '@/components/button/button'; import { @@ -34,7 +34,6 @@ export const TableNode: React.FC> = ({ const { updateTable } = useChartDB(); const edges = useStore((store) => store.edges) as TableEdgeType[]; const { openTableFromSidebar, selectSidebarSection } = useLayout(); - const [isExpanded, setIsExpanded] = useState(false); const selectedEdges = edges.filter( (edge) => (edge.source === id || edge.target === id) && edge.selected @@ -62,20 +61,9 @@ export const TableNode: React.FC> = ({ }); }, [table.id, updateTable]); - const toggleExpand = () => { - setIsExpanded(!isExpanded); - }; - - const visibleFields = isExpanded ? table.fields : table.fields.slice(0, 10); - const hiddenFieldsCount = table.fields.length - 10; - return (
{ if (e.detail === 2) { openTableInEditor(); @@ -126,41 +114,20 @@ export const TableNode: React.FC> = ({
-
- {visibleFields.map((field) => ( - - edge.sourceHandle?.endsWith(field.id) || - edge.targetHandle?.endsWith(field.id) - )} - /> - ))} - {!isExpanded && hiddenFieldsCount > 0 && ( -
- - ... {hiddenFieldsCount} more fields - -
- )} - {isExpanded && ( -
- - Show less - -
- )} -
+ {table.fields.map((field) => ( + + edge.data?.relationship.sourceFieldId === + field.id || + edge.data?.relationship.targetFieldId === field.id + )} + /> + ))} ); }; diff --git a/src/pages/editor-page/side-panel/relationships-section/relationships-section.tsx b/src/pages/editor-page/side-panel/relationships-section/relationships-section.tsx index df7cc513..e7ed2c7f 100644 --- a/src/pages/editor-page/side-panel/relationships-section/relationships-section.tsx +++ b/src/pages/editor-page/side-panel/relationships-section/relationships-section.tsx @@ -4,7 +4,10 @@ import { ListCollapse } from 'lucide-react'; import { Input } from '@/components/input/input'; import { RelationshipList } from './relationship-list/relationship-list'; import { useChartDB } from '@/hooks/use-chartdb'; -import { DBRelationship } from '@/lib/domain/db-relationship'; +import { + DBRelationship, + shouldShowRelationshipBySchemaFilter, +} from '@/lib/domain/db-relationship'; import { useLayout } from '@/hooks/use-layout'; import { EmptyState } from '@/components/empty-state/empty-state'; import { ScrollArea } from '@/components/scroll-area/scroll-area'; @@ -33,11 +36,7 @@ export const RelationshipsSection: React.FC = () => { const filterSchema: (relationship: DBRelationship) => boolean = ( relationship ) => - !filteredSchemas || - !relationship.sourceSchema || - !relationship.targetSchema || - (filteredSchemas.includes(relationship.sourceSchema) && - filteredSchemas.includes(relationship.targetSchema)); + shouldShowRelationshipBySchemaFilter(relationship, filteredSchemas); return relationships.filter(filterSchema).filter(filterName); }, [relationships, filterText, filteredSchemas]); diff --git a/src/pages/editor-page/side-panel/tables-section/tables-section.tsx b/src/pages/editor-page/side-panel/tables-section/tables-section.tsx index eb1513a2..a0e38925 100644 --- a/src/pages/editor-page/side-panel/tables-section/tables-section.tsx +++ b/src/pages/editor-page/side-panel/tables-section/tables-section.tsx @@ -4,7 +4,7 @@ import { Button } from '@/components/button/button'; import { Table, ListCollapse } from 'lucide-react'; import { Input } from '@/components/input/input'; -import { DBTable } from '@/lib/domain/db-table'; +import { DBTable, shouldShowTablesBySchemaFilter } from '@/lib/domain/db-table'; import { useChartDB } from '@/hooks/use-chartdb'; import { useLayout } from '@/hooks/use-layout'; import { EmptyState } from '@/components/empty-state/empty-state'; @@ -30,9 +30,7 @@ export const TablesSection: React.FC = () => { table.name.toLowerCase().includes(filterText.toLowerCase()); const filterSchema: (table: DBTable) => boolean = (table) => - !filteredSchemas || - !table.schema || - filteredSchemas.includes(table.schema); + shouldShowTablesBySchemaFilter(table, filteredSchemas); return tables.filter(filterSchema).filter(filterTableName); }, [tables, filterText, filteredSchemas]);