diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomControls.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomControls.component.tsx index 3f2ab429a2d..21d8de5bd02 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomControls.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomControls.component.tsx @@ -20,11 +20,19 @@ import React, { HTMLAttributes, memo, useCallback, + useEffect, useMemo, useState, } from 'react'; import { FitViewOptions, useReactFlow } from 'reactflow'; import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; +import { + MAX_ZOOM_VALUE, + MIN_ZOOM_VALUE, + ZOOM_BUTTON_STEP, + ZOOM_SLIDER_STEP, + ZOOM_TRANSITION_DURATION, +} from '../../constants/Lineage.constants'; import { getLoadingStatusValue } from '../../utils/EntityLineageUtils'; import SVGIcons, { Icons } from '../../utils/SvgUtils'; @@ -44,6 +52,7 @@ export interface ControlProps extends HTMLAttributes { onExpandColumnClick: () => void; loading: boolean; status: LoadingState; + zoomValue: number; } export const ControlButton: FC> = ({ @@ -75,30 +84,45 @@ const CustomControls: FC = ({ handleFullScreenViewClick, loading, status, + zoomValue, }: ControlProps) => { - const { fitView, zoomIn, zoomOut, zoomTo } = useReactFlow(); - const [zoom, setZoom] = useState(1.5); + const { fitView, zoomTo } = useReactFlow(); + const [zoom, setZoom] = useState(zoomValue); + + const onZoomHandler = useCallback( + (zoomLevel: number) => { + zoomTo?.(zoomLevel, { duration: ZOOM_TRANSITION_DURATION }); + }, + [zoomTo] + ); const onZoomInHandler = useCallback(() => { - setZoom((pre) => (pre < 2.5 ? pre + 0.25 : pre)); - zoomIn?.(); - }, [zoomIn]); + setZoom((pre) => { + const zoomInValue = pre < MAX_ZOOM_VALUE ? pre + ZOOM_BUTTON_STEP : pre; + onZoomHandler(zoomInValue); + + return zoomInValue; + }); + }, [onZoomHandler]); const onZoomOutHandler = useCallback(() => { - setZoom((pre) => (pre > 0.5 ? pre - 0.25 : pre)); - zoomOut?.(); - }, [zoomOut]); + setZoom((pre) => { + const zoomOutValue = pre > MIN_ZOOM_VALUE ? pre - ZOOM_BUTTON_STEP : pre; + onZoomHandler(zoomOutValue); + + return zoomOutValue; + }); + }, [onZoomHandler]); const onFitViewHandler = useCallback(() => { fitView?.(fitViewParams); }, [fitView, fitViewParams]); - const onZoomHandler = useCallback( - (zoomLevel: number) => { - zoomTo?.(zoomLevel); - }, - [zoomTo] - ); + useEffect(() => { + if (zoomValue !== zoom) { + setZoom(zoomValue); + } + }, [zoomValue]); const editIcon = useMemo(() => { return ( @@ -138,11 +162,12 @@ const CustomControls: FC = ({ width="12" /> + { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx index 34c6c389da4..e03a03f8c0d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx @@ -14,6 +14,7 @@ import { Modal } from 'antd'; import { AxiosError } from 'axios'; import { + debounce, isEmpty, isNil, isUndefined, @@ -51,6 +52,7 @@ import { ELEMENT_DELETE_STATE, MAX_ZOOM_VALUE, MIN_ZOOM_VALUE, + ZOOM_VALUE, } from '../../constants/Lineage.constants'; import { EntityType } from '../../enums/entity.enum'; import { @@ -152,6 +154,7 @@ const EntityLineageComponent: FunctionComponent = ({ loading: boolean; status: ElementLoadingState; }>(ELEMENT_DELETE_STATE); + const [zoomValue, setZoomValue] = useState(ZOOM_VALUE); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); @@ -917,6 +920,10 @@ const EntityLineageComponent: FunctionComponent = ({ }); }; + const handleZoomLevel = debounce((value: number) => { + setZoomValue(value); + }, 150); + useEffect(() => { if (!deleted && !isEmpty(updatedLineageData)) { setElementsHandle(updatedLineageData); @@ -979,14 +986,12 @@ const EntityLineageComponent: FunctionComponent = ({ data-testid="react-flow-component" edgeTypes={customEdges} edges={edges} - maxZoom={2} - minZoom={0.5} + maxZoom={MAX_ZOOM_VALUE} + minZoom={MIN_ZOOM_VALUE} nodeTypes={nodeTypes} nodes={nodes} nodesConnectable={isEditMode} selectNodesOnDrag={false} - zoomOnDoubleClick={false} - zoomOnScroll={false} onConnect={onConnect} onDragOver={onDragOver} onDrop={onDrop} @@ -995,6 +1000,7 @@ const EntityLineageComponent: FunctionComponent = ({ onLoad(reactFlowInstance); setReactFlowInstance(reactFlowInstance); }} + onMove={(_e, viewPort) => handleZoomLevel(viewPort.zoom)} onNodeClick={(_e, node) => onNodeClick(node)} onNodeContextMenu={onNodeContextMenu} onNodeDrag={dragHandle} @@ -1017,6 +1023,7 @@ const EntityLineageComponent: FunctionComponent = ({ isEditMode={isEditMode} loading={loading} status={status} + zoomValue={zoomValue} onEditLinageClick={handleEditLineageClick} onExpandColumnClick={handleExpandColumnClick} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/Lineage.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/Lineage.constants.ts index c60dfbd87d7..4c5a815530c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/Lineage.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/Lineage.constants.ts @@ -6,6 +6,9 @@ export const FOREIGN_OBJECT_SIZE = 40; export const ZOOM_VALUE = 1; export const MIN_ZOOM_VALUE = 0.5; export const MAX_ZOOM_VALUE = 2.5; +export const ZOOM_SLIDER_STEP = 0.1; +export const ZOOM_BUTTON_STEP = 0.25; +export const ZOOM_TRANSITION_DURATION = 800; export const entityData = [ {