mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Set service map cursors (#80920)
* Set service map cursors * "pointer" when mousing over a node * "default" when mousing out * "grabbing" while dragging Sets the cursor style on the container, not on the individual element. Since the node can both be clicked (to open a popover) and dragged, I left the cursor on hover as "pointer" to indicate the clickability. Fixes #64283.
This commit is contained in:
parent
f4c596dbe7
commit
dd33002e2f
9 changed files with 362 additions and 32 deletions
|
@ -14,7 +14,7 @@ import { useUrlParams } from '../../../hooks/useUrlParams';
|
|||
import { getAPMHref } from '../../shared/Links/apm/APMLink';
|
||||
import { APMQueryParams } from '../../shared/Links/url_helpers';
|
||||
import { CytoscapeContext } from './Cytoscape';
|
||||
import { getAnimationOptions, getNodeHeight } from './cytoscapeOptions';
|
||||
import { getAnimationOptions, getNodeHeight } from './cytoscape_options';
|
||||
|
||||
const ControlsContainer = styled('div')`
|
||||
left: ${({ theme }) => theme.eui.gutterTypes.gutterMedium};
|
||||
|
|
|
@ -17,7 +17,7 @@ import React, {
|
|||
useState,
|
||||
} from 'react';
|
||||
import { useTheme } from '../../../hooks/useTheme';
|
||||
import { getCytoscapeOptions } from './cytoscapeOptions';
|
||||
import { getCytoscapeOptions } from './cytoscape_options';
|
||||
import { useCytoscapeEventHandlers } from './use_cytoscape_event_handlers';
|
||||
|
||||
cytoscape.use(dagre);
|
||||
|
|
|
@ -22,7 +22,7 @@ import { useTheme } from '../../../../hooks/useTheme';
|
|||
import { fontSize, px } from '../../../../style/variables';
|
||||
import { asInteger, asDuration } from '../../../../../common/utils/formatters';
|
||||
import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink';
|
||||
import { popoverWidth } from '../cytoscapeOptions';
|
||||
import { popoverWidth } from '../cytoscape_options';
|
||||
import { TRANSACTION_REQUEST } from '../../../../../common/transaction_types';
|
||||
import {
|
||||
getSeverity,
|
||||
|
|
|
@ -15,7 +15,7 @@ import React, { MouseEvent } from 'react';
|
|||
import { Buttons } from './Buttons';
|
||||
import { Info } from './Info';
|
||||
import { ServiceStatsFetcher } from './ServiceStatsFetcher';
|
||||
import { popoverWidth } from '../cytoscapeOptions';
|
||||
import { popoverWidth } from '../cytoscape_options';
|
||||
|
||||
interface ContentsProps {
|
||||
isService: boolean;
|
||||
|
|
|
@ -18,7 +18,7 @@ import cytoscape from 'cytoscape';
|
|||
import { useTheme } from '../../../../hooks/useTheme';
|
||||
import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames';
|
||||
import { CytoscapeContext } from '../Cytoscape';
|
||||
import { getAnimationOptions } from '../cytoscapeOptions';
|
||||
import { getAnimationOptions } from '../cytoscape_options';
|
||||
import { Contents } from './Contents';
|
||||
|
||||
interface PopoverProps {
|
||||
|
|
|
@ -5,17 +5,18 @@
|
|||
*/
|
||||
import cytoscape from 'cytoscape';
|
||||
import { CSSProperties } from 'react';
|
||||
import {
|
||||
getServiceHealthStatusColor,
|
||||
ServiceHealthStatus,
|
||||
} from '../../../../common/service_health_status';
|
||||
import { EuiTheme } from '../../../../../observability/public';
|
||||
import { ServiceAnomalyStats } from '../../../../common/anomaly_detection';
|
||||
import {
|
||||
SERVICE_NAME,
|
||||
SPAN_DESTINATION_SERVICE_RESOURCE,
|
||||
} from '../../../../common/elasticsearch_fieldnames';
|
||||
import { EuiTheme } from '../../../../../observability/public';
|
||||
import {
|
||||
getServiceHealthStatusColor,
|
||||
ServiceHealthStatus,
|
||||
} from '../../../../common/service_health_status';
|
||||
import { FETCH_STATUS } from '../../../hooks/useFetcher';
|
||||
import { defaultIcon, iconForNode } from './icons';
|
||||
import { ServiceAnomalyStats } from '../../../../common/anomaly_detection';
|
||||
|
||||
export const popoverWidth = 280;
|
||||
|
||||
|
@ -104,6 +105,11 @@ function isService(el: cytoscape.NodeSingular) {
|
|||
const getStyle = (theme: EuiTheme): cytoscape.Stylesheet[] => {
|
||||
const lineColor = theme.eui.euiColorMediumShade;
|
||||
return [
|
||||
{
|
||||
selector: 'core',
|
||||
// @ts-expect-error DefinitelyTyped does not recognize 'active-bg-opacity'
|
||||
style: { 'active-bg-opacity': 0 },
|
||||
},
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
|
@ -226,7 +232,10 @@ const getStyle = (theme: EuiTheme): cytoscape.Stylesheet[] => {
|
|||
|
||||
// The CSS styles for the div containing the cytoscape element. Makes a
|
||||
// background grid of dots.
|
||||
export const getCytoscapeDivStyle = (theme: EuiTheme): CSSProperties => ({
|
||||
export const getCytoscapeDivStyle = (
|
||||
theme: EuiTheme,
|
||||
status: FETCH_STATUS
|
||||
): CSSProperties => ({
|
||||
background: `linear-gradient(
|
||||
90deg,
|
||||
${theme.eui.euiPageBackgroundColor}
|
||||
|
@ -242,6 +251,7 @@ linear-gradient(
|
|||
center,
|
||||
${theme.eui.euiColorLightShade}`,
|
||||
backgroundSize: `${theme.eui.euiSizeL} ${theme.eui.euiSizeL}`,
|
||||
cursor: `${status === FETCH_STATUS.LOADING ? 'wait' : 'grab'}`,
|
||||
margin: `-${theme.eui.gutterTypes.gutterLarge}`,
|
||||
marginTop: 0,
|
||||
});
|
|
@ -19,7 +19,7 @@ import { callApmApi } from '../../../services/rest/createCallApmApi';
|
|||
import { LicensePrompt } from '../../shared/LicensePrompt';
|
||||
import { Controls } from './Controls';
|
||||
import { Cytoscape } from './Cytoscape';
|
||||
import { getCytoscapeDivStyle } from './cytoscapeOptions';
|
||||
import { getCytoscapeDivStyle } from './cytoscape_options';
|
||||
import { EmptyBanner } from './EmptyBanner';
|
||||
import { EmptyPrompt } from './empty_prompt';
|
||||
import { Popover } from './Popover';
|
||||
|
@ -121,7 +121,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) {
|
|||
elements={data.elements}
|
||||
height={height}
|
||||
serviceName={serviceName}
|
||||
style={getCytoscapeDivStyle(theme)}
|
||||
style={getCytoscapeDivStyle(theme, status)}
|
||||
>
|
||||
<Controls />
|
||||
{serviceName && <EmptyBanner />}
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import cytoscape from 'cytoscape';
|
||||
import { EuiTheme } from '../../../../../observability/public';
|
||||
import { useCytoscapeEventHandlers } from './use_cytoscape_event_handlers';
|
||||
import dagre from 'cytoscape-dagre';
|
||||
import { EuiTheme, useUiTracker } from '../../../../../observability/public';
|
||||
import { useCytoscapeEventHandlers } from './use_cytoscape_event_handlers';
|
||||
import lodash from 'lodash';
|
||||
|
||||
jest.mock('../../../../../observability/public');
|
||||
|
||||
cytoscape.use(dagre);
|
||||
|
||||
|
@ -25,14 +28,109 @@ describe('useCytoscapeEventHandlers', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when data is received', () => {
|
||||
describe('with a service name', () => {
|
||||
it('sets the primary class', () => {
|
||||
const cy = cytoscape({
|
||||
elements: [{ data: { id: 'test' } }],
|
||||
});
|
||||
|
||||
// Mock the chain that leads to layout run
|
||||
jest.spyOn(cy, 'elements').mockReturnValueOnce(({
|
||||
difference: () =>
|
||||
(({
|
||||
layout: () =>
|
||||
(({ run: () => {} } as unknown) as cytoscape.Layouts),
|
||||
} as unknown) as cytoscape.CollectionReturnValue),
|
||||
} as unknown) as cytoscape.CollectionReturnValue);
|
||||
|
||||
renderHook(() =>
|
||||
useCytoscapeEventHandlers({ serviceName: 'test', cy, theme })
|
||||
);
|
||||
cy.trigger('custom:data');
|
||||
|
||||
expect(cy.getElementById('test').hasClass('primary')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('runs the layout', () => {
|
||||
const cy = cytoscape({
|
||||
elements: [{ data: { id: 'test' } }],
|
||||
});
|
||||
const run = jest.fn();
|
||||
|
||||
// Mock the chain that leads to layout run
|
||||
jest.spyOn(cy, 'elements').mockReturnValueOnce(({
|
||||
difference: () =>
|
||||
(({
|
||||
layout: () => (({ run } as unknown) as cytoscape.Layouts),
|
||||
} as unknown) as cytoscape.CollectionReturnValue),
|
||||
} as unknown) as cytoscape.CollectionReturnValue);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.trigger('custom:data');
|
||||
|
||||
expect(run).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layoutstop is triggered', () => {
|
||||
it('applies cubic bézier styles', () => {
|
||||
const cy = cytoscape({
|
||||
elements: [
|
||||
{ data: { id: 'test', source: 'a', target: 'b' } },
|
||||
{ data: { id: 'a' } },
|
||||
{ data: { id: 'b' } },
|
||||
],
|
||||
});
|
||||
const edge = cy.getElementById('test');
|
||||
const style = jest.spyOn(edge, 'style');
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.trigger('layoutstop');
|
||||
|
||||
expect(style).toHaveBeenCalledWith('control-point-distances', [-0, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when an element is dragged', () => {
|
||||
it('sets the hasBeenDragged data', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const node = cy.getElementById('test');
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('drag');
|
||||
node.trigger('drag');
|
||||
|
||||
expect(cy.getElementById('test').data('hasBeenDragged')).toEqual(true);
|
||||
expect(node.data('hasBeenDragged')).toEqual(true);
|
||||
});
|
||||
|
||||
describe('when it has already been dragged', () => {
|
||||
it('keeps hasBeenDragged as true', () => {
|
||||
const cy = cytoscape({
|
||||
elements: [{ data: { hasBeenDragged: true, id: 'test' } }],
|
||||
});
|
||||
const node = cy.getElementById('test');
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
node.trigger('drag');
|
||||
|
||||
expect(node.data('hasBeenDragged')).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a drag ends', () => {
|
||||
it('changes the cursor to pointer', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const container = ({
|
||||
style: { cursor: 'grabbing' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('dragfree');
|
||||
|
||||
expect(container.style.cursor).toEqual('pointer');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -48,6 +146,36 @@ describe('useCytoscapeEventHandlers', () => {
|
|||
|
||||
expect(node.hasClass('hover')).toEqual(true);
|
||||
});
|
||||
|
||||
it('sets the cursor to pointer', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const container = ({
|
||||
style: { cursor: 'default' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('mouseover');
|
||||
|
||||
expect(container.style.cursor).toEqual('pointer');
|
||||
});
|
||||
|
||||
it('tracks an event', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const trackApmEvent = jest.fn();
|
||||
(useUiTracker as jest.Mock).mockReturnValueOnce(trackApmEvent);
|
||||
jest.spyOn(lodash, 'debounce').mockImplementationOnce((fn: any) => {
|
||||
fn();
|
||||
return fn;
|
||||
});
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('mouseover');
|
||||
|
||||
expect(trackApmEvent).toHaveBeenCalledWith({
|
||||
metric: 'service_map_node_or_edge_hover',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a node is un-hovered', () => {
|
||||
|
@ -62,5 +190,157 @@ describe('useCytoscapeEventHandlers', () => {
|
|||
|
||||
expect(node.hasClass('hover')).toEqual(false);
|
||||
});
|
||||
|
||||
it('sets the cursor to the default', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const container = ({
|
||||
style: { cursor: 'pointer' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('mouseout');
|
||||
|
||||
expect(container.style.cursor).toEqual('grab');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when an edge is hovered', () => {
|
||||
it('does not set the cursor to pointer', () => {
|
||||
const cy = cytoscape({
|
||||
elements: [
|
||||
{ data: { id: 'test', source: 'a', target: 'b' } },
|
||||
{ data: { id: 'a' } },
|
||||
{ data: { id: 'b' } },
|
||||
],
|
||||
});
|
||||
const container = ({
|
||||
style: { cursor: 'default' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('mouseover');
|
||||
|
||||
expect(container.style.cursor).toEqual('default');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a node is selected', () => {
|
||||
it('tracks an event', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const trackApmEvent = jest.fn();
|
||||
(useUiTracker as jest.Mock).mockReturnValueOnce(trackApmEvent);
|
||||
jest.spyOn(lodash, 'debounce').mockImplementationOnce((fn: any) => {
|
||||
fn();
|
||||
return fn;
|
||||
});
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('select');
|
||||
|
||||
expect(trackApmEvent).toHaveBeenCalledWith({
|
||||
metric: 'service_map_node_select',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a node is unselected', () => {
|
||||
it('resets connected edge styles', () => {
|
||||
const cy = cytoscape({
|
||||
elements: [
|
||||
{ data: { id: 'test' } },
|
||||
{ data: { id: 'edge', source: 'test', target: 'test2' } },
|
||||
{ data: { id: 'test2' } },
|
||||
],
|
||||
});
|
||||
|
||||
renderHook(() =>
|
||||
useCytoscapeEventHandlers({
|
||||
serviceName: 'test',
|
||||
cy,
|
||||
theme,
|
||||
})
|
||||
);
|
||||
cy.getElementById('test').trigger('unselect');
|
||||
|
||||
expect(cy.getElementById('edge').hasClass('highlight')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a tap starts', () => {
|
||||
it('sets the cursor to grabbing', () => {
|
||||
const cy = cytoscape({});
|
||||
const container = ({
|
||||
style: { cursor: 'grab' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.trigger('tapstart');
|
||||
|
||||
expect(container.style.cursor).toEqual('grabbing');
|
||||
});
|
||||
|
||||
describe('when the target is a node', () => {
|
||||
it('does not change the cursor', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const container = ({
|
||||
style: { cursor: 'grab' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('tapstart');
|
||||
|
||||
expect(container.style.cursor).toEqual('grab');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a tap ends', () => {
|
||||
it('sets the cursor to the default', () => {
|
||||
const cy = cytoscape({});
|
||||
const container = ({
|
||||
style: { cursor: 'grabbing' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.trigger('tapend');
|
||||
|
||||
expect(container.style.cursor).toEqual('grab');
|
||||
});
|
||||
|
||||
describe('when the target is a node', () => {
|
||||
it('does not change the cursor', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
const container = ({
|
||||
style: { cursor: 'pointer' },
|
||||
} as unknown) as HTMLElement;
|
||||
jest.spyOn(cy, 'container').mockReturnValueOnce(container);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('tapend');
|
||||
|
||||
expect(container.style.cursor).toEqual('pointer');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when debug is enabled', () => {
|
||||
it('logs a debug message', () => {
|
||||
const cy = cytoscape({ elements: [{ data: { id: 'test' } }] });
|
||||
(useUiTracker as jest.Mock).mockReturnValueOnce(() => {});
|
||||
jest.spyOn(Storage.prototype, 'getItem').mockReturnValueOnce('true');
|
||||
const debug = jest
|
||||
.spyOn(window.console, 'debug')
|
||||
.mockReturnValueOnce(undefined);
|
||||
|
||||
renderHook(() => useCytoscapeEventHandlers({ cy, theme }));
|
||||
cy.getElementById('test').trigger('select');
|
||||
|
||||
expect(debug).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ import cytoscape from 'cytoscape';
|
|||
import { debounce } from 'lodash';
|
||||
import { useEffect } from 'react';
|
||||
import { EuiTheme, useUiTracker } from '../../../../../observability/public';
|
||||
import { getAnimationOptions, getNodeHeight } from './cytoscapeOptions';
|
||||
import { getAnimationOptions, getNodeHeight } from './cytoscape_options';
|
||||
|
||||
/*
|
||||
* @notice
|
||||
|
@ -66,6 +66,24 @@ function getLayoutOptions({
|
|||
};
|
||||
}
|
||||
|
||||
function setCursor(cursor: string, event: cytoscape.EventObjectCore) {
|
||||
const container = event.cy.container();
|
||||
|
||||
if (container) {
|
||||
container.style.cursor = cursor;
|
||||
}
|
||||
}
|
||||
|
||||
function resetConnectedEdgeStyle(
|
||||
cytoscapeInstance: cytoscape.Core,
|
||||
node?: cytoscape.NodeSingular
|
||||
) {
|
||||
cytoscapeInstance.edges().removeClass('highlight');
|
||||
if (node) {
|
||||
node.connectedEdges().addClass('highlight');
|
||||
}
|
||||
}
|
||||
|
||||
export function useCytoscapeEventHandlers({
|
||||
cy,
|
||||
serviceName,
|
||||
|
@ -80,16 +98,6 @@ export function useCytoscapeEventHandlers({
|
|||
useEffect(() => {
|
||||
const nodeHeight = getNodeHeight(theme);
|
||||
|
||||
const resetConnectedEdgeStyle = (
|
||||
cytoscapeInstance: cytoscape.Core,
|
||||
node?: cytoscape.NodeSingular
|
||||
) => {
|
||||
cytoscapeInstance.edges().removeClass('highlight');
|
||||
if (node) {
|
||||
node.connectedEdges().addClass('highlight');
|
||||
}
|
||||
};
|
||||
|
||||
const dataHandler: cytoscape.EventHandler = (event, fit) => {
|
||||
if (serviceName) {
|
||||
const node = event.cy.getElementById(serviceName);
|
||||
|
@ -123,11 +131,17 @@ export function useCytoscapeEventHandlers({
|
|||
);
|
||||
|
||||
const mouseoverHandler: cytoscape.EventHandler = (event) => {
|
||||
if (event.target.isNode()) {
|
||||
setCursor('pointer', event);
|
||||
}
|
||||
|
||||
trackNodeEdgeHover();
|
||||
event.target.addClass('hover');
|
||||
event.target.connectedEdges().addClass('nodeHover');
|
||||
};
|
||||
const mouseoutHandler: cytoscape.EventHandler = (event) => {
|
||||
setCursor('grab', event);
|
||||
|
||||
event.target.removeClass('hover');
|
||||
event.target.connectedEdges().removeClass('nodeHover');
|
||||
};
|
||||
|
@ -148,17 +162,37 @@ export function useCytoscapeEventHandlers({
|
|||
console.debug('cytoscape:', event);
|
||||
}
|
||||
};
|
||||
|
||||
const dragHandler: cytoscape.EventHandler = (event) => {
|
||||
setCursor('grabbing', event);
|
||||
|
||||
applyCubicBezierStyles(event.target.connectedEdges());
|
||||
|
||||
if (!event.target.data('hasBeenDragged')) {
|
||||
event.target.data('hasBeenDragged', true);
|
||||
}
|
||||
};
|
||||
const dragfreeHandler: cytoscape.EventHandler = (event) => {
|
||||
setCursor('pointer', event);
|
||||
};
|
||||
const tapstartHandler: cytoscape.EventHandler = (event) => {
|
||||
// Onle set cursot to "grabbing" if the target doesn't have an "isNode"
|
||||
// property (meaning it's the canvas) or if "isNode" is false (meaning
|
||||
// it's an edge.)
|
||||
if (!event.target.isNode || !event.target.isNode()) {
|
||||
setCursor('grabbing', event);
|
||||
}
|
||||
};
|
||||
const tapendHandler: cytoscape.EventHandler = (event) => {
|
||||
if (!event.target.isNode || !event.target.isNode()) {
|
||||
setCursor('grab', event);
|
||||
}
|
||||
};
|
||||
|
||||
if (cy) {
|
||||
cy.on('custom:data drag layoutstop select unselect', debugHandler);
|
||||
cy.on(
|
||||
'custom:data drag dragfree layoutstop select tapstart tapend unselect',
|
||||
debugHandler
|
||||
);
|
||||
cy.on('custom:data', dataHandler);
|
||||
cy.on('layoutstop', layoutstopHandler);
|
||||
cy.on('mouseover', 'edge, node', mouseoverHandler);
|
||||
|
@ -166,12 +200,15 @@ export function useCytoscapeEventHandlers({
|
|||
cy.on('select', 'node', selectHandler);
|
||||
cy.on('unselect', 'node', unselectHandler);
|
||||
cy.on('drag', 'node', dragHandler);
|
||||
cy.on('dragfree', 'node', dragfreeHandler);
|
||||
cy.on('tapstart', tapstartHandler);
|
||||
cy.on('tapend', tapendHandler);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (cy) {
|
||||
cy.removeListener(
|
||||
'custom:data drag layoutstop select unselect',
|
||||
'custom:data drag dragfree layoutstop select tapstart tapend unselect',
|
||||
undefined,
|
||||
debugHandler
|
||||
);
|
||||
|
@ -182,6 +219,9 @@ export function useCytoscapeEventHandlers({
|
|||
cy.removeListener('select', 'node', selectHandler);
|
||||
cy.removeListener('unselect', 'node', unselectHandler);
|
||||
cy.removeListener('drag', 'node', dragHandler);
|
||||
cy.removeListener('dragfree', 'node', dragfreeHandler);
|
||||
cy.removeListener('tapstart', undefined, tapstartHandler);
|
||||
cy.removeListener('tapend', undefined, tapendHandler);
|
||||
}
|
||||
};
|
||||
}, [cy, serviceName, trackApmEvent, theme]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue