Skip to content

Commit

Permalink
temp
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Oct 31, 2024
1 parent 2a1b615 commit 1b60821
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DragObjectWithType,
Node,
withPanZoom,
withDragZoom,
GraphComponent,
withCreateConnector,
Graph,
Expand Down Expand Up @@ -60,7 +61,7 @@ const demoComponentFactory: ComponentFactory = (
type: string
): React.ComponentType<{ element: GraphElement }> | undefined => {
if (kind === ModelKind.graph) {
return withDndDrop(graphDropTargetSpec([NODE_DRAG_TYPE]))(withPanZoom()(GraphComponent));
return withDndDrop(graphDropTargetSpec([NODE_DRAG_TYPE]))(withPanZoom()(withDragZoom()(GraphComponent)));
}
switch (type) {
case 'node':
Expand Down
20 changes: 10 additions & 10 deletions packages/demo-app-ts/src/demos/topologyPackageDemo/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export const graphPositionChangeListener: EventListener = ({ graph }): void => {
const scaleExtent = graph.getScaleExtent();

// eslint-disable-next-line no-console
console.log(
`Graph Position Change:\n Position: ${Math.round(position.x)},${Math.round(
position.y
)}\n Scale: ${scale}\n Scale Extent: max: ${scaleExtent[0]} max: ${scaleExtent[1]}`
);
// console.log(
// `Graph Position Change:\n Position: ${Math.round(position.x)},${Math.round(
// position.y
// )}\n Scale: ${scale}\n Scale Extent: max: ${scaleExtent[0]} max: ${scaleExtent[1]}`
// );

// After an interval, check that what we got was the final value.
if (positionTimer) {
Expand All @@ -31,11 +31,11 @@ export const graphPositionChangeListener: EventListener = ({ graph }): void => {
}
if (newPosition.x !== position.x || newPosition.y !== position.y) {
// eslint-disable-next-line no-console
console.error(
`Graph Position Changed: ${Math.round(position.x)},${Math.round(position.y)} => ${Math.round(
newPosition.x
)},${Math.round(newPosition.y)}`
);
// console.error(
// `Graph Position Changed: ${Math.round(position.x)},${Math.round(position.y)} => ${Math.round(
// newPosition.x
// )},${Math.round(newPosition.y)}`
// );
}
if (newScaleExtent !== scaleExtent) {
// eslint-disable-next-line no-console
Expand Down
1 change: 1 addition & 0 deletions packages/module/src/behavior/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './useDndDrop';
export * from './useDndManager';
export * from './useDragNode';
export * from './usePanZoom';
export * from './useDragZoom';
export * from './useReconnect';
export * from './useSelection';
export * from './usePolygonAnchor';
Expand Down
2 changes: 2 additions & 0 deletions packages/module/src/behavior/useDndManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ export class DndManagerImpl implements DndManager {
private performHitTests(): void {
const draggedItemType = this.getItemType();
const event = this.getDragEvent();
// eslint-disable-next-line no-console
console.log(`DragEvent: ${event.x},${event.y}`);
if (event && draggedItemType) {
const targetIds: string[] = [];
Object.keys(this.targets).forEach((targetId) => {
Expand Down
181 changes: 181 additions & 0 deletions packages/module/src/behavior/useDragZoom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
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, isGraph } from '../types';
import { PanZoomRef } from './usePanZoom';
import Point from '../geom/Point';
// import Rect from '../geom/Rect';

export type DragZoomRef = (node: SVGGElement | null) => void;

// Used to send events prevented by d3.zoom to the document allowing modals, dropdowns, etc, to close
const propagateDragZoomMouseEvent = (e: Event): void => {
document.dispatchEvent(new MouseEvent(e.type, e));
};

export const useDragZoom = (): WithDragZoomProps => {
const element = React.useContext(ElementContext);
const [draggingState, setDraggingState] = React.useState<Omit<WithDragZoomProps, 'dragZoomRef'>>({});

if (!isGraph(element)) {
throw new Error('useDragZoom must be used within the scope of a Graph');
}
const elementRef = React.useRef<Graph>(element);
elementRef.current = element;

const zoomToState = (dragStart: Point, dragEnd: Point) => {
const currentScale = elementRef.current.getScale();
const graphPosition = elementRef.current.getPosition();

const start = dragStart.clone(); // .scale(elementRef.current.getScale());
const end = dragEnd.clone(); // .scale(currentScale);
const x = -1 * graphPosition.x + Math.min(start.x, end.x); // graphPosition.x * currentScale + Math.min(start.x, end.x) / currentScale;
const y = -1 * graphPosition.y + Math.min(start.y, end.y); // graphPosition.y * currentScale + Math.min(start.y, end.y) / currentScale;
const width = Math.abs(end.x - start.x) / currentScale;
const height = Math.abs(end.y - start.y) / currentScale;

if (width < 10 || height < 0) {
return;
}

// eslint-disable-next-line no-console
console.log(`currentPosition: ${graphPosition.x},${graphPosition.y}`, currentScale);
// eslint-disable-next-line no-console
console.log(`Dimensions: ${elementRef.current.getDimensions().width},${elementRef.current.getDimensions().height}`);
// eslint-disable-next-line no-console

// eslint-disable-next-line no-console
console.log(`Drag: ${dragStart.x},${dragStart.y} => ${dragEnd.x},${dragEnd.y}`);
// eslint-disable-next-line no-console
console.log(`Translate to: ${x},${y} => ${width}x${height}`);

const { width: fullWidth, height: fullHeight } = elementRef.current.getDimensions();
// const midX = x + width / 2;
// const midY = y + height / 2;

// compute the scale
const xScale = fullWidth / width;
const yScale = fullHeight / height;
const scale = Math.min(xScale, yScale);

// const tx = fullWidth / 2 - midX * scale;
// const ty = fullHeight / 2 - midY * scale;

// eslint-disable-next-line no-console
console.log(`Zoom to: ${x},${y} ${scale}`);
// TODO should scale and bound be kept in a single geom Transform object instead of separately?
elementRef.current.setScale(scale);
elementRef.current.setPosition(new Point(x, y));

// const { width: fullWidth, height: fullHeight } = elementRef.current.getDimensions();
// // const midX = x + width / 2;
// // const midY = y + height / 2;
//
// const xScale = fullWidth / width;
// const yScale = fullHeight / height;
// const zoomScale = Math.min(xScale, yScale);
// // const zoomScale = 1 / Math.max(width / Math.max(1, fullWidth), height / Math.max(1, fullHeight));
//
// // translate to center
// // const tx = fullWidth / 2 - midX;
// // const ty = fullHeight / 2 - midY;
//
// // eslint-disable-next-line no-console
// console.log(`Zoom Scale: `, zoomScale);
// // eslint-disable-next-line no-console
// console.log(`Zoom Position: ${x},${y}`);
//
// elementRef.current.setScale(zoomScale);
// elementRef.current.setPosition(new Point(x, y));
};

const dragZoomRef = useCallbackRef<PanZoomRef>((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', propagateDragZoomMouseEvent);
node.ownerSVGElement.addEventListener('click', propagateDragZoomMouseEvent);
}
const drag = d3
.drag()
.on(
'start',
action((event: d3.D3DragEvent<Element, any, any>) => {
// eslint-disable-next-line no-console
console.log(event);
const { offsetX, offsetY } =
event.sourceEvent instanceof MouseEvent ? event.sourceEvent : { offsetX: 0, offsetY: 0 };
const { width: maxX, height: maxY } = elementRef.current.getDimensions();
// eslint-disable-next-line no-console
console.log(`Graph Position: ${elementRef.current.getPosition().x},${elementRef.current.getPosition().y}`);
// eslint-disable-next-line no-console
console.log(`Offset: ${offsetX},${offsetY}`);
const startPoint = new Point(Math.min(Math.max(offsetX, 0), maxX), Math.min(Math.max(offsetY, 0), maxY));
setDraggingState({
isZoomDragging: true,
zoomDragStart: startPoint,
zoomDragEnd: startPoint
});
})
)
.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();
// eslint-disable-next-line no-console
// console.log(`Offset: ${offsetX},${offsetY}`);
setDraggingState((prev) => ({
...prev,
zoomDragEnd: new Point(Math.min(Math.max(offsetX, 0), maxX), Math.min(Math.max(offsetY, 0), maxY))
}));
})
)
.on(
'end',
action(() => {
setDraggingState((prev) => {
zoomToState(prev.zoomDragStart, prev.zoomDragEnd);
return { isZoomDragging: false };
});
})
)
.filter((event: React.MouseEvent) => event.ctrlKey && !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', propagateDragZoomMouseEvent);
node.ownerSVGElement.removeEventListener('click', propagateDragZoomMouseEvent);
}
}
};
});
return { dragZoomRef, ...draggingState };
};
export interface WithDragZoomProps {
dragZoomRef?: DragZoomRef;
isZoomDragging?: boolean;
zoomDragStart?: Point;
zoomDragEnd?: Point;
}

export const withDragZoom =
() =>
<P extends WithDragZoomProps>(WrappedComponent: React.ComponentType<P>) => {
const Component: React.FunctionComponent<Omit<P, keyof WithDragZoomProps>> = (props) => {
const dragZoomProps = useDragZoom();
return <WrappedComponent {...(props as any)} {...dragZoomProps} />;
};
Component.displayName = `withPanZoom(${WrappedComponent.displayName || WrappedComponent.name})`;
return observer(Component);
};
2 changes: 1 addition & 1 deletion packages/module/src/behavior/usePanZoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const usePanZoom = (): PanZoomRef => {
})
)
.filter((event: React.MouseEvent) => {
if (event.ctrlKey || event.button) {
if (event.ctrlKey || event.shiftKey || event.button) {
return false;
}
// only allow zoom from double clicking the graph directly
Expand Down
23 changes: 21 additions & 2 deletions packages/module/src/components/GraphComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import * as React from 'react';
import { observer } from 'mobx-react';
import { Graph, isGraph } from '../types';
import { WithPanZoomProps } from '../behavior/usePanZoom';
import { WithDragZoomProps } from '../behavior/useDragZoom';
import { WithDndDropProps } from '../behavior/useDndDrop';
import { WithSelectionProps } from '../behavior/useSelection';
import { WithContextMenuProps } from '../behavior/withContextMenu';
import LayersProvider from './layers/LayersProvider';
import ElementWrapper from './ElementWrapper';
import { GraphElementProps } from './factories';
import useCombineRefs from '../utils/useCombineRefs';

type GraphComponentProps = GraphElementProps &
WithPanZoomProps &
WithDragZoomProps &
WithDndDropProps &
WithSelectionProps &
WithContextMenuProps;
Expand Down Expand Up @@ -39,15 +42,21 @@ const Inner: React.FunctionComponent<{ element: Graph }> = React.memo(
const GraphComponent: React.FunctionComponent<GraphComponentProps> = ({
element,
panZoomRef,
dragZoomRef,
dndDropRef,
onSelect,
onContextMenu
onContextMenu,
isZoomDragging,
zoomDragStart,
zoomDragEnd
}) => {
const zoomRefs = useCombineRefs(panZoomRef, dragZoomRef);
if (!isGraph(element)) {
return null;
}
const graphElement = element as Graph;
const { x, y, width, height } = graphElement.getBounds();

return (
<>
<rect
Expand All @@ -60,9 +69,19 @@ const GraphComponent: React.FunctionComponent<GraphComponentProps> = ({
onClick={onSelect}
onContextMenu={onContextMenu}
/>
<g data-surface="true" ref={panZoomRef} transform={`translate(${x}, ${y}) scale(${graphElement.getScale()})`}>
<g data-surface="true" ref={zoomRefs} transform={`translate(${x}, ${y}) scale(${graphElement.getScale()})`}>
<Inner element={graphElement} />
</g>
{isZoomDragging && zoomDragStart && zoomDragEnd ? (
<rect
x={Math.min(zoomDragStart.x, zoomDragEnd.x)}
y={Math.min(zoomDragStart.y, zoomDragEnd.y)}
width={Math.abs(zoomDragEnd.x - zoomDragStart.x)}
height={Math.abs(zoomDragEnd.y - zoomDragStart.y)}
fill="red"
opacity={0.2}
/>
) : null}
</>
);
};
Expand Down

0 comments on commit 1b60821

Please sign in to comment.