From fb54d7346562199db67817970fbbdc6e117514b9 Mon Sep 17 00:00:00 2001 From: johnnyfish Date: Thu, 31 Jul 2025 23:00:49 +0300 Subject: [PATCH] refactor: consolidate table filtering to canvas filter with improved UX --- .../canvas-context/canvas-provider.tsx | 49 +++++++- src/context/layout-context/layout-context.tsx | 8 -- .../layout-context/layout-provider.tsx | 10 -- .../local-config-context.tsx | 8 -- .../local-config-provider.tsx | 16 --- .../canvas/canvas-filter/canvas-filter.tsx | 117 ++++++++++++++---- .../editor-page/canvas/toolbar/toolbar.tsx | 21 +++- src/pages/editor-page/editor-page.tsx | 90 +------------- .../dependencies-section.tsx | 14 ++- .../relationships-section.tsx | 15 ++- .../editor-page/side-panel/side-panel.tsx | 65 +--------- .../table-list-item-header.tsx | 3 +- .../tables-section/tables-section.tsx | 11 +- 13 files changed, 192 insertions(+), 235 deletions(-) diff --git a/src/context/canvas-context/canvas-provider.tsx b/src/context/canvas-context/canvas-provider.tsx index ab819991..0401148b 100644 --- a/src/context/canvas-context/canvas-provider.tsx +++ b/src/context/canvas-context/canvas-provider.tsx @@ -1,4 +1,11 @@ -import React, { type ReactNode, useCallback, useState } from 'react'; +import React, { + type ReactNode, + useCallback, + useState, + useMemo, + useEffect, + useRef, +} from 'react'; import { canvasContext } from './canvas-context'; import { useChartDB } from '@/hooks/use-chartdb'; import { @@ -15,13 +22,49 @@ interface CanvasProviderProps { } export const CanvasProvider = ({ children }: CanvasProviderProps) => { - const { tables, relationships, updateTablesState, filteredSchemas } = - useChartDB(); + const { + tables, + relationships, + updateTablesState, + filteredSchemas, + hiddenTableIds, + schemas, + } = useChartDB(); const { fitView } = useReactFlow(); const [overlapGraph, setOverlapGraph] = useState>(createGraph()); + // Check if there are any filtered items to determine initial showFilter state + const hasFilteredItems = useMemo(() => { + const hasHiddenTables = (hiddenTableIds ?? []).length > 0; + const hasSchemasFilter = + filteredSchemas && + schemas.length > 0 && + filteredSchemas.length < schemas.length; + return hasHiddenTables || hasSchemasFilter; + }, [filteredSchemas, hiddenTableIds, schemas]); + const [showFilter, setShowFilter] = useState(false); + const hasInitialized = useRef(false); + + // Only auto-show filter on initial load if there are filtered items + // Wait for data to be defined (not just empty arrays) before initializing + useEffect(() => { + const dataLoaded = + filteredSchemas !== undefined && hiddenTableIds !== undefined; + + if (!hasInitialized.current && dataLoaded) { + // Add 2 seconds delay to ensure all data is fully loaded + const timer = setTimeout(() => { + if (hasFilteredItems) { + setShowFilter(true); + } + hasInitialized.current = true; + }, 2000); + + return () => clearTimeout(timer); + } + }, [hasFilteredItems, filteredSchemas, hiddenTableIds]); const reorderTables = useCallback( ( diff --git a/src/context/layout-context/layout-context.tsx b/src/context/layout-context/layout-context.tsx index 12885e5a..a3c2df1a 100644 --- a/src/context/layout-context/layout-context.tsx +++ b/src/context/layout-context/layout-context.tsx @@ -36,10 +36,6 @@ export interface LayoutContext { hideSidePanel: () => void; showSidePanel: () => void; toggleSidePanel: () => void; - - isSelectSchemaOpen: boolean; - openSelectSchema: () => void; - closeSelectSchema: () => void; } export const layoutContext = createContext({ @@ -70,8 +66,4 @@ export const layoutContext = createContext({ hideSidePanel: emptyFn, showSidePanel: emptyFn, toggleSidePanel: emptyFn, - - isSelectSchemaOpen: false, - openSelectSchema: emptyFn, - closeSelectSchema: emptyFn, }); diff --git a/src/context/layout-context/layout-provider.tsx b/src/context/layout-context/layout-provider.tsx index d1db4cb3..c7aec87e 100644 --- a/src/context/layout-context/layout-provider.tsx +++ b/src/context/layout-context/layout-provider.tsx @@ -23,8 +23,6 @@ export const LayoutProvider: React.FC = ({ React.useState('tables'); const [isSidePanelShowed, setIsSidePanelShowed] = React.useState(isDesktop); - const [isSelectSchemaOpen, setIsSelectSchemaOpen] = - React.useState(false); const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] = () => setOpenedTableInSidebar(''); @@ -88,11 +86,6 @@ export const LayoutProvider: React.FC = ({ setOpenedTableInSidebar(customTypeId); }; - const openSelectSchema: LayoutContext['openSelectSchema'] = () => - setIsSelectSchemaOpen(true); - - const closeSelectSchema: LayoutContext['closeSelectSchema'] = () => - setIsSelectSchemaOpen(false); return ( = ({ hideSidePanel, showSidePanel, toggleSidePanel, - isSelectSchemaOpen, - openSelectSchema, - closeSelectSchema, openedDependencyInSidebar, openDependencyFromSidebar, closeAllDependenciesInSidebar, diff --git a/src/context/local-config-context/local-config-context.tsx b/src/context/local-config-context/local-config-context.tsx index c0f2fb0d..cebe90a8 100644 --- a/src/context/local-config-context/local-config-context.tsx +++ b/src/context/local-config-context/local-config-context.tsx @@ -22,11 +22,6 @@ export interface LocalConfigContext { showFieldAttributes: boolean; setShowFieldAttributes: (showFieldAttributes: boolean) => void; - hideMultiSchemaNotification: boolean; - setHideMultiSchemaNotification: ( - hideMultiSchemaNotification: boolean - ) => void; - githubRepoOpened: boolean; setGithubRepoOpened: (githubRepoOpened: boolean) => void; @@ -56,9 +51,6 @@ export const LocalConfigContext = createContext({ showFieldAttributes: true, setShowFieldAttributes: emptyFn, - hideMultiSchemaNotification: false, - setHideMultiSchemaNotification: emptyFn, - githubRepoOpened: false, setGithubRepoOpened: emptyFn, diff --git a/src/context/local-config-context/local-config-provider.tsx b/src/context/local-config-context/local-config-provider.tsx index b582e03a..9d0a47ec 100644 --- a/src/context/local-config-context/local-config-provider.tsx +++ b/src/context/local-config-context/local-config-provider.tsx @@ -8,7 +8,6 @@ const scrollActionKey = 'scroll_action'; const schemasFilterKey = 'schemas_filter'; const showCardinalityKey = 'show_cardinality'; const showFieldAttributesKey = 'show_field_attributes'; -const hideMultiSchemaNotificationKey = 'hide_multi_schema_notification'; const githubRepoOpenedKey = 'github_repo_opened'; const starUsDialogLastOpenKey = 'star_us_dialog_last_open'; const showDependenciesOnCanvasKey = 'show_dependencies_on_canvas'; @@ -40,12 +39,6 @@ export const LocalConfigProvider: React.FC = ({ (localStorage.getItem(showFieldAttributesKey) || 'true') === 'true' ); - const [hideMultiSchemaNotification, setHideMultiSchemaNotification] = - React.useState( - (localStorage.getItem(hideMultiSchemaNotificationKey) || - 'false') === 'true' - ); - const [githubRepoOpened, setGithubRepoOpened] = React.useState( (localStorage.getItem(githubRepoOpenedKey) || 'false') === 'true' ); @@ -77,13 +70,6 @@ export const LocalConfigProvider: React.FC = ({ localStorage.setItem(githubRepoOpenedKey, githubRepoOpened.toString()); }, [githubRepoOpened]); - useEffect(() => { - localStorage.setItem( - hideMultiSchemaNotificationKey, - hideMultiSchemaNotification.toString() - ); - }, [hideMultiSchemaNotification]); - useEffect(() => { localStorage.setItem(themeKey, theme); }, [theme]); @@ -127,8 +113,6 @@ export const LocalConfigProvider: React.FC = ({ setShowCardinality, showFieldAttributes, setShowFieldAttributes, - hideMultiSchemaNotification, - setHideMultiSchemaNotification, setGithubRepoOpened, githubRepoOpened, starUsDialogLastOpen, diff --git a/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx b/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx index 005d559c..be4071e2 100644 --- a/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx +++ b/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx @@ -93,9 +93,18 @@ export const CanvasFilter: React.FC = ({ onClose }) => { tablesBySchema.forEach((schemaTables, schemaName) => { const schemaId = schemaNameToSchemaId(schemaName); - const schemaHidden = filteredSchemas + const schemaFilteredOut = filteredSchemas ? !filteredSchemas.includes(schemaId) : false; + + // Pre-calculate if all tables in this schema are hidden + const allTablesHidden = schemaTables.every( + (table) => hiddenTableIds?.includes(table.id) ?? false + ); + + // Schema appears hidden if filtered out OR all tables are hidden + const schemaHidden = schemaFilteredOut || allTablesHidden; + const schemaNode: TreeNode = { id: `schema-${schemaName}`, name: `${schemaName} (${schemaTables.length})`, @@ -136,13 +145,24 @@ export const CanvasFilter: React.FC = ({ onClose }) => { return nodes; }, [relevantTableData, databaseType, hiddenTableIds, filteredSchemas]); - // Initialize expanded state with all schemas expanded - useMemo(() => { - const initialExpanded: Record = {}; - treeData.forEach((node) => { - initialExpanded[node.id] = true; + // Initialize expanded state - collapse if multiple schemas, expand if single schema + useEffect(() => { + setExpanded((prevExpanded) => { + const hasMultipleSchemas = treeData.length > 1; + const newExpanded: Record = {}; + + treeData.forEach((node) => { + // Preserve existing expanded state if it exists, otherwise set based on schema count + if (node.id in prevExpanded) { + newExpanded[node.id] = prevExpanded[node.id]; + } else { + // If there are multiple schemas, start collapsed; otherwise expanded + newExpanded[node.id] = !hasMultipleSchemas; + } + }); + + return newExpanded; }); - setExpanded(initialExpanded); }, [treeData]); // Filter tree data based on search query @@ -222,10 +242,19 @@ export const CanvasFilter: React.FC = ({ onClose }) => { if (node.type === 'schema') { const schemaContext = node.context as SchemaContext; const schemaId = schemaNameToSchemaId(schemaContext.name); - const schemaHidden = filteredSchemas + const schemaFilteredOut = filteredSchemas ? !filteredSchemas.includes(schemaId) : false; + // Check if all tables in this schema are hidden + const allTablesHidden = + node.children?.every((child) => + hiddenTableIds?.includes(child.id) + ) ?? false; + + // Schema is "hidden" if filtered out OR all tables are hidden + const schemaHidden = schemaFilteredOut || allTablesHidden; + return (