-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(graph): Add selection based zoom
- Loading branch information
1 parent
2a1b615
commit 4ec7379
Showing
11 changed files
with
381 additions
and
7 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
packages/demo-app-ts/src/demos/topologyPackageDemo/AreaDragHint.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
.area-drag-hint__hint-container { | ||
justify-content: center; | ||
display: flex; | ||
pointer-events: none; | ||
position: absolute; | ||
top: var(--pf-v5-global--spacer--sm); | ||
left: 0; | ||
right: 0; | ||
} | ||
.area-drag-hint__hint-background { | ||
background-color: var(--pf-v5-global--BackgroundColor--100); | ||
border: 1px solid var(--pf-v5-global--BorderColor--light-100); | ||
border-radius: 8px; | ||
padding: var(--pf-v5-global--spacer--xs) var(--pf-v5-global--spacer--sm); | ||
pointer-events: none; | ||
} | ||
|
||
.area-drag-hint { | ||
align-items: center; | ||
display: flex; | ||
} | ||
.area-drag-hint__icon { | ||
color: var(--pf-v5-global--palette--blue-300); | ||
} | ||
.area-drag-hint__text { | ||
margin-left: var(--pf-v5-global--spacer--sm); | ||
} | ||
.area-drag-hint-shortcut__cell { | ||
padding-left: var(--pf-v5-global--spacer--sm); | ||
} | ||
|
||
.area-drag-hint-shortcut__command:not(:last-child):after { | ||
content: ' + '; | ||
} | ||
|
||
.area-drag-hint-shortcut__kbd { | ||
border: var(--pf-v5-global--BorderWidth--sm) solid var(--pf-v5-global--BorderColor--100); | ||
border-radius: 3px; | ||
color: var(--pf-v5-global--Color--200); | ||
font-size: var(--pf-v5-global--FontSize--sm); | ||
padding: 1px 3px; | ||
} |
50 changes: 50 additions & 0 deletions
50
packages/demo-app-ts/src/demos/topologyPackageDemo/AreaDragHint.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import * as React from 'react'; | ||
import { InfoCircleIcon, MouseIcon } from '@patternfly/react-icons'; | ||
|
||
import './AreaDragHint.css'; | ||
|
||
const AreaDragHint: React.FC = () => { | ||
return ( | ||
<div className="area-drag-hint__hint-container"> | ||
<div className="area-drag-hint__hint-background"> | ||
<div className="area-drag-hint"> | ||
<InfoCircleIcon className="area-drag-hint__icon" /> | ||
<span className="area-drag-hint__text"> | ||
<table> | ||
<tbody> | ||
<tr> | ||
<td className="area-drag-hint-shortcut__cell"> | ||
<span className="area-drag-hint-shortcut__command"> | ||
<kbd className="area-drag-hint-shortcut__kbd">Shift</kbd> | ||
</span> | ||
<span className="area-drag-hint-shortcut__command"> | ||
<kbd className="area-drag-hint-shortcut__kbd"> | ||
<MouseIcon /> Drag | ||
</kbd> | ||
</span> | ||
</td> | ||
<td className="area-drag-hint-shortcut__cell">Select nodes in area</td> | ||
</tr> | ||
<tr> | ||
<td className="area-drag-hint-shortcut__cell"> | ||
<span className="area-drag-hint-shortcut__command"> | ||
<kbd className="area-drag-hint-shortcut__kbd">Ctrl</kbd> | ||
</span> | ||
<span className="area-drag-hint-shortcut__command"> | ||
<kbd className="area-drag-hint-shortcut__kbd"> | ||
<MouseIcon /> Drag | ||
</kbd> | ||
</span> | ||
</td> | ||
<td className="area-drag-hint-shortcut__cell">Zoom to selected area</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AreaDragHint; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import * as React from 'react'; | ||
import * as d3 from 'd3'; | ||
import { observer } from 'mobx-react'; | ||
import { action } from 'mobx'; | ||
import ElementContext from '../utils/ElementContext'; | ||
import useCallbackRef from '../utils/useCallbackRef'; | ||
import { Graph, GRAPH_AREA_DRAGGING_EVENT, GRAPH_AREA_SELECTED_EVENT, isGraph, ModifierKey } from '../types'; | ||
import Point from '../geom/Point'; | ||
|
||
export type AreaSelectionRef = (node: SVGGElement | null) => void; | ||
|
||
// Used to send events prevented by d3.zoom to the document allowing modals, dropdowns, etc, to close | ||
const propagateAreaSelectionMouseEvent = (e: Event): void => { | ||
document.dispatchEvent(new MouseEvent(e.type, e)); | ||
}; | ||
|
||
export const useAreaSelection = (modifiers: ModifierKey[] = ['ctrlKey']): WithAreaSelectionProps => { | ||
const element = React.useContext(ElementContext); | ||
const [draggingState, setDraggingState] = React.useState<Omit<WithAreaSelectionProps, 'areaSelectionRef'>>({}); | ||
|
||
if (!isGraph(element)) { | ||
throw new Error('useAreaSelection must be used within the scope of a Graph'); | ||
} | ||
const elementRef = React.useRef<Graph>(element); | ||
elementRef.current = element; | ||
|
||
const areaSelectionRef = useCallbackRef<AreaSelectionRef>((node: SVGGElement | null) => { | ||
if (node) { | ||
// TODO fix any type | ||
const $svg = d3.select(node.ownerSVGElement) as any; | ||
if (node && node.ownerSVGElement) { | ||
node.ownerSVGElement.addEventListener('mousedown', propagateAreaSelectionMouseEvent); | ||
node.ownerSVGElement.addEventListener('click', propagateAreaSelectionMouseEvent); | ||
} | ||
const drag = d3 | ||
.drag() | ||
.on( | ||
'start', | ||
action((event: d3.D3DragEvent<Element, any, any>) => { | ||
const { offsetX, offsetY } = | ||
event.sourceEvent instanceof MouseEvent ? event.sourceEvent : { offsetX: 0, offsetY: 0 }; | ||
const { width: maxX, height: maxY } = elementRef.current.getDimensions(); | ||
|
||
const startPoint = new Point(Math.min(Math.max(offsetX, 0), maxX), Math.min(Math.max(offsetY, 0), maxY)); | ||
const modifier = modifiers.find((m) => event.sourceEvent[m]); | ||
|
||
setDraggingState({ | ||
modifier, | ||
isAreaSelectDragging: true, | ||
areaSelectDragStart: startPoint, | ||
areaSelectDragEnd: startPoint | ||
}); | ||
elementRef.current | ||
.getController() | ||
.fireEvent(GRAPH_AREA_DRAGGING_EVENT, { graph: elementRef.current, isDragging: true }); | ||
}) | ||
) | ||
.on( | ||
'drag', | ||
action((event: d3.D3DragEvent<Element, any, any>) => { | ||
const { offsetX, offsetY } = | ||
event.sourceEvent instanceof MouseEvent ? event.sourceEvent : { offsetX: 0, offsetY: 0 }; | ||
const { width: maxX, height: maxY } = elementRef.current.getDimensions(); | ||
setDraggingState((prev) => ({ | ||
...prev, | ||
areaSelectDragEnd: new Point(Math.min(Math.max(offsetX, 0), maxX), Math.min(Math.max(offsetY, 0), maxY)) | ||
})); | ||
}) | ||
) | ||
.on( | ||
'end', | ||
action(() => { | ||
setDraggingState((prev) => { | ||
elementRef.current.getController().fireEvent(GRAPH_AREA_SELECTED_EVENT, { | ||
graph: elementRef.current, | ||
modifier: prev.modifier, | ||
startPoint: prev.areaSelectDragStart, | ||
endPoint: prev.areaSelectDragEnd | ||
}); | ||
return { isAreaSelectDragging: false }; | ||
}); | ||
elementRef.current | ||
.getController() | ||
.fireEvent(GRAPH_AREA_DRAGGING_EVENT, { graph: elementRef.current, isDragging: false }); | ||
}) | ||
) | ||
.filter((event: React.MouseEvent) => modifiers.find((m) => event[m]) && !event.button); | ||
drag($svg); | ||
} | ||
|
||
return () => { | ||
if (node) { | ||
// remove all drag listeners | ||
d3.select(node.ownerSVGElement).on('.drag', null); | ||
if (node.ownerSVGElement) { | ||
node.ownerSVGElement.removeEventListener('mousedown', propagateAreaSelectionMouseEvent); | ||
node.ownerSVGElement.removeEventListener('click', propagateAreaSelectionMouseEvent); | ||
} | ||
} | ||
}; | ||
}); | ||
return { areaSelectionRef, ...draggingState }; | ||
}; | ||
export interface WithAreaSelectionProps { | ||
areaSelectionRef?: AreaSelectionRef; | ||
modifier?: ModifierKey; | ||
isAreaSelectDragging?: boolean; | ||
areaSelectDragStart?: Point; | ||
areaSelectDragEnd?: Point; | ||
} | ||
|
||
export const withAreaSelection = | ||
(modifier: ModifierKey[] = ['ctrlKey']) => | ||
<P extends WithAreaSelectionProps>(WrappedComponent: React.ComponentType<P>) => { | ||
const Component: React.FunctionComponent<Omit<P, keyof WithAreaSelectionProps>> = (props) => { | ||
const areaSelectionProps = useAreaSelection(modifier); | ||
return <WrappedComponent {...(props as any)} {...areaSelectionProps} />; | ||
}; | ||
Component.displayName = `withAreaSelection(${WrappedComponent.displayName || WrappedComponent.name})`; | ||
return observer(Component); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.