mirror of
https://github.com/chartdb/chartdb.git
synced 2025-10-22 23:01:56 +00:00
feat: add zoom navigation buttons to canvas filter for tables and areas (#903)
* feat: add zoom navigation buttons to canvas filter for tables and areas * fix * fix --------- Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
142
src/hooks/use-focus-on.ts
Normal file
142
src/hooks/use-focus-on.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
|
||||
interface FocusOptions {
|
||||
select?: boolean;
|
||||
}
|
||||
|
||||
export const useFocusOn = () => {
|
||||
const { fitView, setNodes, setEdges } = useReactFlow();
|
||||
const { hideSidePanel } = useLayout();
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
|
||||
const focusOnArea = useCallback(
|
||||
(areaId: string, options: FocusOptions = {}) => {
|
||||
const { select = true } = options;
|
||||
|
||||
if (select) {
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id === areaId
|
||||
? {
|
||||
...node,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: areaId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[fitView, setNodes, hideSidePanel, isDesktop]
|
||||
);
|
||||
|
||||
const focusOnTable = useCallback(
|
||||
(tableId: string, options: FocusOptions = {}) => {
|
||||
const { select = true } = options;
|
||||
|
||||
if (select) {
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id === tableId
|
||||
? {
|
||||
...node,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: tableId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[fitView, setNodes, hideSidePanel, isDesktop]
|
||||
);
|
||||
|
||||
const focusOnRelationship = useCallback(
|
||||
(
|
||||
relationshipId: string,
|
||||
sourceTableId: string,
|
||||
targetTableId: string,
|
||||
options: FocusOptions = {}
|
||||
) => {
|
||||
const { select = true } = options;
|
||||
|
||||
if (select) {
|
||||
setEdges((edges) =>
|
||||
edges.map((edge) =>
|
||||
edge.id === relationshipId
|
||||
? {
|
||||
...edge,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...edge,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: sourceTableId,
|
||||
},
|
||||
{
|
||||
id: targetTableId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[fitView, setEdges, hideSidePanel, isDesktop]
|
||||
);
|
||||
|
||||
return {
|
||||
focusOnArea,
|
||||
focusOnTable,
|
||||
focusOnRelationship,
|
||||
};
|
||||
};
|
@@ -47,7 +47,7 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
||||
addTablesToFilter,
|
||||
removeTablesFromFilter,
|
||||
} = useDiagramFilter();
|
||||
const { fitView, setNodes } = useReactFlow();
|
||||
const { setNodes } = useReactFlow();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
||||
const [isFilterVisible, setIsFilterVisible] = useState(false);
|
||||
@@ -160,39 +160,53 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
||||
]
|
||||
);
|
||||
|
||||
const focusOnTable = useCallback(
|
||||
const selectTable = useCallback(
|
||||
(tableId: string) => {
|
||||
// Make sure the table is visible
|
||||
// Make sure the table is visible, selected and trigger animation
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id === tableId
|
||||
? {
|
||||
...node,
|
||||
hidden: false,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
nodes.map((node) => {
|
||||
if (node.id === tableId) {
|
||||
return {
|
||||
...node,
|
||||
selected: true,
|
||||
data: {
|
||||
...node.data,
|
||||
highlightTable: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...node,
|
||||
selected: false,
|
||||
data: {
|
||||
...node.data,
|
||||
highlightTable: false,
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Focus on the table
|
||||
// Remove the highlight flag after animation completes
|
||||
setTimeout(() => {
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: tableId,
|
||||
},
|
||||
],
|
||||
});
|
||||
}, 100);
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) => {
|
||||
if (node.id === tableId) {
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
highlightTable: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return node;
|
||||
})
|
||||
);
|
||||
}, 600);
|
||||
},
|
||||
[fitView, setNodes]
|
||||
[setNodes]
|
||||
);
|
||||
|
||||
// Handle node click
|
||||
@@ -202,13 +216,13 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
||||
const context = node.context as TableContext;
|
||||
const isTableVisible = context.visible;
|
||||
|
||||
// Only focus if table is visible
|
||||
// Only select if table is visible
|
||||
if (isTableVisible) {
|
||||
focusOnTable(node.id);
|
||||
selectTable(node.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
[focusOnTable]
|
||||
[selectTable]
|
||||
);
|
||||
|
||||
// Animate in on mount and focus search input
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
import { Eye, EyeOff, CircleDotDashed } from 'lucide-react';
|
||||
import { Button } from '@/components/button/button';
|
||||
import type { TreeNode } from '@/components/tree-view/tree';
|
||||
import { schemaNameToSchemaId } from '@/lib/domain/db-schema';
|
||||
import { useFocusOn } from '@/hooks/use-focus-on';
|
||||
import type {
|
||||
AreaContext,
|
||||
NodeContext,
|
||||
@@ -12,6 +13,7 @@ import type {
|
||||
TableContext,
|
||||
} from './types';
|
||||
import type { FilterTableInfo } from '@/lib/domain/diagram-filter/diagram-filter';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface FilterItemActionsProps {
|
||||
node: TreeNode<NodeType, NodeContext>;
|
||||
@@ -40,6 +42,7 @@ export const FilterItemActions: React.FC<FilterItemActionsProps> = ({
|
||||
addTablesToFilter,
|
||||
removeTablesFromFilter,
|
||||
}) => {
|
||||
const { focusOnArea, focusOnTable } = useFocusOn();
|
||||
if (node.type === 'schema') {
|
||||
const context = node.context as SchemaContext;
|
||||
const schemaVisible = context.visible;
|
||||
@@ -50,7 +53,7 @@ export const FilterItemActions: React.FC<FilterItemActionsProps> = ({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="size-7 h-fit p-0"
|
||||
className="h-fit w-6 p-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -67,9 +70,9 @@ export const FilterItemActions: React.FC<FilterItemActionsProps> = ({
|
||||
}}
|
||||
>
|
||||
{!schemaVisible ? (
|
||||
<EyeOff className="size-3.5 text-muted-foreground" />
|
||||
<EyeOff className="!size-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<Eye className="size-3.5" />
|
||||
<Eye className="!size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
@@ -81,37 +84,60 @@ export const FilterItemActions: React.FC<FilterItemActionsProps> = ({
|
||||
const isUngrouped = context.isUngrouped;
|
||||
const areaId = context.id;
|
||||
|
||||
const handleZoomToArea = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (!isUngrouped) {
|
||||
focusOnArea(areaId);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="size-7 h-fit p-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Toggle all tables in this area
|
||||
if (areaVisible) {
|
||||
// Hide all tables in this area
|
||||
removeTablesFromFilter({
|
||||
filterCallback: (table) =>
|
||||
(isUngrouped && !table.areaId) ||
|
||||
(!isUngrouped && table.areaId === areaId),
|
||||
});
|
||||
} else {
|
||||
// Show all tables in this area
|
||||
addTablesToFilter({
|
||||
filterCallback: (table) =>
|
||||
(isUngrouped && !table.areaId) ||
|
||||
(!isUngrouped && table.areaId === areaId),
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{!areaVisible ? (
|
||||
<EyeOff className="size-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<Eye className="size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
<div className="flex h-full items-center gap-0.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
'flex h-fit w-6 items-center justify-center p-0 opacity-0 transition-opacity group-hover:opacity-100',
|
||||
{
|
||||
'!opacity-0': !areaVisible,
|
||||
}
|
||||
)}
|
||||
onClick={handleZoomToArea}
|
||||
disabled={!areaVisible}
|
||||
>
|
||||
<CircleDotDashed className="!size-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="flex h-fit w-6 items-center justify-center p-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Toggle all tables in this area
|
||||
if (areaVisible) {
|
||||
// Hide all tables in this area
|
||||
removeTablesFromFilter({
|
||||
filterCallback: (table) =>
|
||||
(isUngrouped && !table.areaId) ||
|
||||
(!isUngrouped && table.areaId === areaId),
|
||||
});
|
||||
} else {
|
||||
// Show all tables in this area
|
||||
addTablesToFilter({
|
||||
filterCallback: (table) =>
|
||||
(isUngrouped && !table.areaId) ||
|
||||
(!isUngrouped && table.areaId === areaId),
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{!areaVisible ? (
|
||||
<EyeOff className="!size-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<Eye className="!size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,22 +146,43 @@ export const FilterItemActions: React.FC<FilterItemActionsProps> = ({
|
||||
const context = node.context as TableContext;
|
||||
const tableVisible = context.visible;
|
||||
|
||||
const handleZoomToTable = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
focusOnTable(tableId);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="size-7 h-fit p-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleTableFilter(tableId);
|
||||
}}
|
||||
>
|
||||
{!tableVisible ? (
|
||||
<EyeOff className="size-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<Eye className="size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
<div className="flex h-full items-center gap-0.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
'flex h-fit w-6 items-center justify-center p-0 opacity-0 transition-opacity group-hover:opacity-100',
|
||||
{
|
||||
'!opacity-0': !tableVisible,
|
||||
}
|
||||
)}
|
||||
onClick={handleZoomToTable}
|
||||
disabled={!tableVisible}
|
||||
>
|
||||
<CircleDotDashed className="!size-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="flex w-6 items-center justify-center p-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleTableFilter(tableId);
|
||||
}}
|
||||
>
|
||||
{!tableVisible ? (
|
||||
<EyeOff className="!size-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<Eye className="!size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,7 @@ export type TableNodeType = Node<
|
||||
isOverlapping: boolean;
|
||||
highlightOverlappingTables?: boolean;
|
||||
hasHighlightedCustomType?: boolean;
|
||||
highlightTable?: boolean;
|
||||
},
|
||||
'table'
|
||||
>;
|
||||
@@ -68,6 +69,7 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
|
||||
isOverlapping,
|
||||
highlightOverlappingTables,
|
||||
hasHighlightedCustomType,
|
||||
highlightTable,
|
||||
},
|
||||
}) => {
|
||||
const { updateTable, relationships, readonly } = useChartDB();
|
||||
@@ -321,6 +323,9 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
|
||||
hasHighlightedCustomType
|
||||
? 'ring-2 ring-offset-slate-50 dark:ring-offset-slate-900 ring-yellow-500 ring-offset-2 animate-scale'
|
||||
: '',
|
||||
highlightTable
|
||||
? 'ring-2 ring-offset-slate-50 dark:ring-offset-slate-900 ring-blue-500 ring-offset-2 animate-scale-2'
|
||||
: '',
|
||||
isDiffTableChanged &&
|
||||
!isSummaryOnly &&
|
||||
!isDiffNewTable &&
|
||||
@@ -339,6 +344,7 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
|
||||
isOverlapping,
|
||||
highlightOverlappingTables,
|
||||
hasHighlightedCustomType,
|
||||
highlightTable,
|
||||
isSummaryOnly,
|
||||
isDiffTableChanged,
|
||||
isDiffNewTable,
|
||||
|
@@ -31,9 +31,7 @@ import {
|
||||
} from '@/components/dropdown-menu/dropdown-menu';
|
||||
import { ListItemHeaderButton } from '@/pages/editor-page/side-panel/list-item-header-button/list-item-header-button';
|
||||
import { mergeRefs } from '@/lib/utils';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useFocusOn } from '@/hooks/use-focus-on';
|
||||
|
||||
export interface AreaListItemProps {
|
||||
area: Area;
|
||||
@@ -43,9 +41,7 @@ export const AreaListItem = React.forwardRef<HTMLDivElement, AreaListItemProps>(
|
||||
({ area }, forwardedRef) => {
|
||||
const { updateArea, removeArea, readonly } = useChartDB();
|
||||
const { t } = useTranslation();
|
||||
const { fitView, setNodes } = useReactFlow();
|
||||
const { hideSidePanel } = useLayout();
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
const { focusOnArea } = useFocusOn();
|
||||
const [editMode, setEditMode] = React.useState(false);
|
||||
const [areaName, setAreaName] = React.useState(area.name);
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
@@ -92,38 +88,12 @@ export const AreaListItem = React.forwardRef<HTMLDivElement, AreaListItemProps>(
|
||||
[area.id, updateArea]
|
||||
);
|
||||
|
||||
const focusOnArea = useCallback(
|
||||
const handleFocusOnArea = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.stopPropagation();
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id == area.id
|
||||
? {
|
||||
...node,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: area.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
focusOnArea(area.id);
|
||||
},
|
||||
[fitView, area.id, setNodes, hideSidePanel, isDesktop]
|
||||
[focusOnArea, area.id]
|
||||
);
|
||||
|
||||
useClickAway(inputRef, saveAreaName);
|
||||
@@ -241,7 +211,9 @@ export const AreaListItem = React.forwardRef<HTMLDivElement, AreaListItemProps>(
|
||||
disabled={readonly}
|
||||
/>
|
||||
<div className="hidden md:group-hover:flex">
|
||||
<ListItemHeaderButton onClick={focusOnArea}>
|
||||
<ListItemHeaderButton
|
||||
onClick={handleFocusOnArea}
|
||||
>
|
||||
<CircleDotDashed />
|
||||
</ListItemHeaderButton>
|
||||
</div>
|
||||
|
@@ -10,6 +10,7 @@ import { ListItemHeaderButton } from '../../../../list-item-header-button/list-i
|
||||
import type { DBRelationship } from '@/lib/domain/db-relationship';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { useFocusOn } from '@/hooks/use-focus-on';
|
||||
import { useClickAway, useKeyPressEvent } from 'react-use';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -21,8 +22,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/dropdown-menu/dropdown-menu';
|
||||
import { Input } from '@/components/input/input';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface RelationshipListItemHeaderProps {
|
||||
@@ -33,11 +32,10 @@ export const RelationshipListItemHeader: React.FC<
|
||||
RelationshipListItemHeaderProps
|
||||
> = ({ relationship }) => {
|
||||
const { updateRelationship, removeRelationship, readonly } = useChartDB();
|
||||
const { fitView, deleteElements, setEdges } = useReactFlow();
|
||||
const { deleteElements } = useReactFlow();
|
||||
const { t } = useTranslation();
|
||||
const { hideSidePanel } = useLayout();
|
||||
const { focusOnRelationship } = useFocusOn();
|
||||
const [editMode, setEditMode] = React.useState(false);
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
const [relationshipName, setRelationshipName] = React.useState(
|
||||
relationship.name
|
||||
);
|
||||
@@ -70,48 +68,20 @@ export const RelationshipListItemHeader: React.FC<
|
||||
setEditMode(true);
|
||||
};
|
||||
|
||||
const focusOnRelationship = useCallback(
|
||||
const handleFocusOnRelationship = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.stopPropagation();
|
||||
setEdges((edges) =>
|
||||
edges.map((edge) =>
|
||||
edge.id == relationship.id
|
||||
? {
|
||||
...edge,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...edge,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
focusOnRelationship(
|
||||
relationship.id,
|
||||
relationship.sourceTableId,
|
||||
relationship.targetTableId
|
||||
);
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: relationship.sourceTableId,
|
||||
},
|
||||
{
|
||||
id: relationship.targetTableId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[
|
||||
fitView,
|
||||
focusOnRelationship,
|
||||
relationship.id,
|
||||
relationship.sourceTableId,
|
||||
relationship.targetTableId,
|
||||
setEdges,
|
||||
relationship.id,
|
||||
isDesktop,
|
||||
hideSidePanel,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -182,7 +152,9 @@ export const RelationshipListItemHeader: React.FC<
|
||||
<Pencil />
|
||||
</ListItemHeaderButton>
|
||||
) : null}
|
||||
<ListItemHeaderButton onClick={focusOnRelationship}>
|
||||
<ListItemHeaderButton
|
||||
onClick={handleFocusOnRelationship}
|
||||
>
|
||||
<CircleDotDashed />
|
||||
</ListItemHeaderButton>
|
||||
</div>
|
||||
|
@@ -26,9 +26,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/dropdown-menu/dropdown-menu';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useFocusOn } from '@/hooks/use-focus-on';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDialog } from '@/hooks/use-dialog';
|
||||
import {
|
||||
@@ -62,11 +60,9 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
|
||||
const { schemasDisplayed } = useDiagramFilter();
|
||||
const { openTableSchemaDialog } = useDialog();
|
||||
const { t } = useTranslation();
|
||||
const { fitView, setNodes } = useReactFlow();
|
||||
const { hideSidePanel } = useLayout();
|
||||
const { focusOnTable } = useFocusOn();
|
||||
const [editMode, setEditMode] = React.useState(false);
|
||||
const [tableName, setTableName] = React.useState(table.name);
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const { listeners } = useSortable({ id: table.id });
|
||||
|
||||
@@ -93,38 +89,12 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
|
||||
setEditMode(true);
|
||||
};
|
||||
|
||||
const focusOnTable = useCallback(
|
||||
const handleFocusOnTable = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.stopPropagation();
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id == table.id
|
||||
? {
|
||||
...node,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: table.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
focusOnTable(table.id);
|
||||
},
|
||||
[fitView, table.id, setNodes, hideSidePanel, isDesktop]
|
||||
[focusOnTable, table.id]
|
||||
);
|
||||
|
||||
const deleteTableHandler = useCallback(() => {
|
||||
@@ -339,7 +309,7 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
|
||||
<Pencil />
|
||||
</ListItemHeaderButton>
|
||||
) : null}
|
||||
<ListItemHeaderButton onClick={focusOnTable}>
|
||||
<ListItemHeaderButton onClick={handleFocusOnTable}>
|
||||
<CircleDotDashed />
|
||||
</ListItemHeaderButton>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user