From 84e55f5df654be47c27f17fac55359d6a454d87d Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Sat, 3 Jan 2026 13:10:43 +0200 Subject: [PATCH] fix(filter): show visible/total count badge for schema and area groups (#1045) --- src/components/tree-view/tree-view.tsx | 18 +- src/components/tree-view/tree.ts | 1 + .../editor-page/canvas/canvas-filter/utils.ts | 227 ++++++------------ 3 files changed, 94 insertions(+), 152 deletions(-) diff --git a/src/components/tree-view/tree-view.tsx b/src/components/tree-view/tree-view.tsx index 6bdb181a..e396963f 100644 --- a/src/components/tree-view/tree-view.tsx +++ b/src/components/tree-view/tree-view.tsx @@ -354,13 +354,27 @@ function TreeNode>({ - {node.empty ? '' : node.name} + + {node.empty ? '' : node.name} + + {node.suffix && ( + + {node.suffix} + + )} {renderActionsComponent && renderActionsComponent(node)} {isHovered && renderHoverComponent diff --git a/src/components/tree-view/tree.ts b/src/components/tree-view/tree.ts index 71287f3f..74078190 100644 --- a/src/components/tree-view/tree.ts +++ b/src/components/tree-view/tree.ts @@ -12,6 +12,7 @@ export interface TreeNode< icon?: LucideIcon; iconProps?: React.ComponentProps; labelProps?: React.ComponentProps<'span'>; + suffix?: string; type: Type; unselectable?: boolean; tooltip?: string; diff --git a/src/pages/editor-page/canvas/canvas-filter/utils.ts b/src/pages/editor-page/canvas/canvas-filter/utils.ts index e0acd036..e99ed4a3 100644 --- a/src/pages/editor-page/canvas/canvas-filter/utils.ts +++ b/src/pages/editor-page/canvas/canvas-filter/utils.ts @@ -13,6 +13,43 @@ import { Box, Database, Layers, Table } from 'lucide-react'; import { filterTable } from '@/lib/domain/diagram-filter/filter'; import { defaultSchemas } from '@/lib/data/default-schemas'; +type TableWithVisibility = RelevantTableData & { visible: boolean }; + +const computeTableVisibility = ( + tables: RelevantTableData[], + filter: DiagramFilter | undefined, + databaseType: DatabaseType +): TableWithVisibility[] => + tables.map((table) => ({ + ...table, + visible: filterTable({ + table: { + id: table.id, + schema: table.schema, + }, + filter, + options: { + defaultSchema: defaultSchemas[databaseType], + }, + }), + })); + +const createTableChildren = ( + tablesWithVisibility: TableWithVisibility[] +): TreeNode[] => + tablesWithVisibility.map((table) => ({ + id: table.id, + name: table.name, + type: 'table' as const, + isFolder: false, + icon: Table, + context: { + tableSchema: table.schema, + visible: table.visible, + } satisfies TableContext, + className: !table.visible ? 'opacity-50' : '', + })); + export const generateTreeDataByAreas = ({ areas, databaseType, @@ -50,27 +87,23 @@ export const generateTreeDataByAreas = ({ // Create nodes for areas areas.forEach((area) => { const areaTables = tablesByArea.get(area.id) || []; + if (areaTables.length === 0) return; - // Check if at least one table in the area is visible - const areaVisible = - // areaTables.length === 0 || - !areaTables.some( - (table) => - filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }) === false - ); + // Pre-compute visibility for all tables in this area (single pass) + const tablesWithVisibility = computeTableVisibility( + areaTables, + filter, + databaseType + ); + const visibleCount = tablesWithVisibility.filter( + (t) => t.visible + ).length; + const areaVisible = visibleCount === areaTables.length; const areaNode: TreeNode = { id: `area-${area.id}`, - name: `${area.name} (${areaTables.length})`, + name: area.name, + suffix: `${visibleCount}/${areaTables.length}`, type: 'area', isFolder: true, icon: Box, @@ -81,59 +114,28 @@ export const generateTreeDataByAreas = ({ isUngrouped: false, } satisfies AreaContext, className: !areaVisible ? 'opacity-50' : '', - children: areaTables.map( - (table): TreeNode => { - const tableVisible = filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }); - - return { - id: table.id, - name: table.name, - type: 'table', - isFolder: false, - icon: Table, - context: { - tableSchema: table.schema, - visible: tableVisible, - } satisfies TableContext, - className: !tableVisible ? 'opacity-50' : '', - }; - } - ), + children: createTableChildren(tablesWithVisibility), }; - if (areaTables.length > 0) { - nodes.push(areaNode); - } + nodes.push(areaNode); }); // Add ungrouped tables if (tablesWithoutArea.length > 0) { - const ungroupedVisible = !tablesWithoutArea.some( - (table) => - filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }) == false + const tablesWithVisibility = computeTableVisibility( + tablesWithoutArea, + filter, + databaseType ); + const visibleCount = tablesWithVisibility.filter( + (t) => t.visible + ).length; + const ungroupedVisible = visibleCount === tablesWithoutArea.length; const ungroupedNode: TreeNode = { id: 'ungrouped', - name: `Ungrouped (${tablesWithoutArea.length})`, + name: 'Ungrouped', + suffix: `${visibleCount}/${tablesWithoutArea.length}`, type: 'area', isFolder: true, icon: Layers, @@ -144,33 +146,7 @@ export const generateTreeDataByAreas = ({ isUngrouped: true, } satisfies AreaContext, className: !ungroupedVisible ? 'opacity-50' : '', - children: tablesWithoutArea.map( - (table): TreeNode => { - const tableVisible = filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }); - - return { - id: table.id, - name: table.name, - type: 'table', - isFolder: false, - icon: Table, - context: { - tableSchema: table.schema, - visible: tableVisible, - } satisfies TableContext, - className: !tableVisible ? 'opacity-50' : '', - }; - } - ), + children: createTableChildren(tablesWithVisibility), }; nodes.push(ungroupedNode); } @@ -191,7 +167,7 @@ export const generateTreeDataBySchemas = ({ }): TreeNode[] => { const nodes: TreeNode[] = []; - // Group tables by schema (existing logic) + // Group tables by schema const tablesBySchema = new Map(); relevantTableData.forEach((table) => { @@ -211,42 +187,21 @@ export const generateTreeDataBySchemas = ({ }); tablesBySchema.forEach((schemaTables, schemaName) => { - let schemaVisible; - - if (databaseWithSchemas) { - schemaVisible = !schemaTables.some( - (table) => - filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }) === false - ); - } else { - // if at least one table is visible, the schema is considered visible - schemaVisible = !schemaTables.some( - (table) => - filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }) === false - ); - } + // Pre-compute visibility for all tables in this schema (single pass) + const tablesWithVisibility = computeTableVisibility( + schemaTables, + filter, + databaseType + ); + const visibleCount = tablesWithVisibility.filter( + (t) => t.visible + ).length; + const schemaVisible = visibleCount === schemaTables.length; const schemaNode: TreeNode = { id: `schema-${schemaName}`, - name: `${schemaName} (${schemaTables.length})`, + name: schemaName, + suffix: `${visibleCount}/${schemaTables.length}`, type: 'schema', isFolder: true, icon: Database, @@ -255,35 +210,7 @@ export const generateTreeDataBySchemas = ({ visible: schemaVisible, } satisfies SchemaContext, className: !schemaVisible ? 'opacity-50' : '', - children: schemaTables.map( - (table): TreeNode => { - const tableVisible = filterTable({ - table: { - id: table.id, - schema: table.schema, - }, - filter, - options: { - defaultSchema: defaultSchemas[databaseType], - }, - }); - - const hidden = !tableVisible; - - return { - id: table.id, - name: table.name, - type: 'table', - isFolder: false, - icon: Table, - context: { - tableSchema: table.schema, - visible: tableVisible, - } satisfies TableContext, - className: hidden ? 'opacity-50' : '', - }; - } - ), + children: createTableChildren(tablesWithVisibility), }; nodes.push(schemaNode); });