refactor(callbacks): dont provide noops as defaults, but make cbs optional

This commit is contained in:
moklick
2020-05-25 16:24:50 +02:00
parent 7324c79fd0
commit 17614bda6b
8 changed files with 118 additions and 78 deletions
+5 -2
View File
@@ -13,7 +13,7 @@ interface EdgeWrapperProps {
labelStyle?: CSSProperties; labelStyle?: CSSProperties;
labelShowBg?: boolean; labelShowBg?: boolean;
labelBgStyle: CSSProperties; labelBgStyle: CSSProperties;
onClick: (edge: Edge) => void; onClick?: (edge: Edge) => void;
animated: boolean; animated: boolean;
selected: boolean; selected: boolean;
isInteractive: boolean; isInteractive: boolean;
@@ -46,7 +46,10 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
} }
store.dispatch.setSelectedElements({ id, source, target }); store.dispatch.setSelectedElements({ id, source, target });
onClick({ id, source, target, type });
if (onClick) {
onClick({ id, source, target, type });
}
}; };
return ( return (
+65 -33
View File
@@ -10,16 +10,27 @@ import { Node, XYPosition, Transform, ElementId, NodeComponentProps, WrapNodePro
const getMouseEvent = (evt: MouseEvent | TouchEvent) => const getMouseEvent = (evt: MouseEvent | TouchEvent) =>
typeof TouchEvent !== 'undefined' && evt instanceof TouchEvent ? evt.touches[0] : (evt as MouseEvent); typeof TouchEvent !== 'undefined' && evt instanceof TouchEvent ? evt.touches[0] : (evt as MouseEvent);
const onStart = ( interface OnDragStartParams {
evt: MouseEvent | TouchEvent, evt: MouseEvent | TouchEvent;
onDragStart: (node: Node) => void, id: ElementId;
id: ElementId, type: string;
type: string, data: any;
data: any, setOffset: (pos: XYPosition) => void;
setOffset: (pos: XYPosition) => void, transform: Transform;
transform: Transform, position: XYPosition;
position: XYPosition onNodeDragStart?: (node: Node) => void;
): false | void => { }
const onStart = ({
evt,
onNodeDragStart,
id,
type,
data,
setOffset,
transform,
position,
}: OnDragStartParams): false | void => {
const startEvt = getMouseEvent(evt); const startEvt = getMouseEvent(evt);
const scaledClient: XYPosition = { const scaledClient: XYPosition = {
@@ -33,16 +44,21 @@ const onStart = (
store.dispatch.setSelectedElements({ id, type } as Node); store.dispatch.setSelectedElements({ id, type } as Node);
setOffset({ x: offsetX, y: offsetY }); setOffset({ x: offsetX, y: offsetY });
onDragStart(node);
if (onNodeDragStart) {
onNodeDragStart(node);
}
}; };
const onDrag = ( interface OnDragParams {
evt: MouseEvent | TouchEvent, evt: MouseEvent | TouchEvent;
setDragging: (isDragging: boolean) => void, setDragging: (isDragging: boolean) => void;
id: ElementId, id: ElementId;
offset: XYPosition, offset: XYPosition;
transform: Transform transform: Transform;
): void => { }
const onDrag = ({ evt, setDragging, id, offset, transform }: OnDragParams): void => {
const dragEvt = getMouseEvent(evt); const dragEvt = getMouseEvent(evt);
const scaledClient = { const scaledClient = {
@@ -60,16 +76,27 @@ const onDrag = (
}); });
}; };
const onStop = ( interface OnDragStopParams {
onDragStop: (node: Node) => void, isDragging: boolean;
onClick: (node: Node) => void, setDragging: (isDragging: boolean) => void;
isDragging: boolean, id: ElementId;
setDragging: (isDragging: boolean) => void, type: string;
id: ElementId, position: XYPosition;
type: string, data: any;
position: XYPosition, onNodeDragStop?: (node: Node) => void;
data: any onClick?: (node: Node) => void;
): void => { }
const onStop = ({
onNodeDragStop,
onClick,
isDragging,
setDragging,
id,
type,
position,
data,
}: OnDragStopParams): void => {
const node = { const node = {
id, id,
type, type,
@@ -77,12 +104,15 @@ const onStop = (
data, data,
} as Node; } as Node;
if (!isDragging) { if (!isDragging && onClick) {
return onClick(node); return onClick(node);
} }
setDragging(false); setDragging(false);
onDragStop(node);
if (onNodeDragStop) {
onNodeDragStop(node);
}
}; };
export default (NodeComponent: ComponentType<NodeComponentProps>) => { export default (NodeComponent: ComponentType<NodeComponentProps>) => {
@@ -146,9 +176,11 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
return ( return (
<DraggableCore <DraggableCore
onStart={(evt) => onStart(evt as MouseEvent, onNodeDragStart, id, type, data, setOffset, transform, position)} onStart={(evt) =>
onDrag={(evt) => onDrag(evt as MouseEvent, setDragging, id, offset, transform)} onStart({ evt: evt as MouseEvent, onNodeDragStart, id, type, data, setOffset, transform, position })
onStop={() => onStop(onNodeDragStop, onClick, isDragging, setDragging, id, type, position, data)} }
onDrag={(evt) => onDrag({ evt: evt as MouseEvent, setDragging, id, offset, transform })}
onStop={() => onStop({ onNodeDragStop, onClick, isDragging, setDragging, id, type, position, data })}
scale={transform[2]} scale={transform[2]}
disabled={!isInteractive} disabled={!isInteractive}
cancel=".nodrag" cancel=".nodrag"
+14 -11
View File
@@ -16,13 +16,13 @@ import { Elements, NodeTypesType, EdgeTypesType, OnLoadFunc, Node, Edge, Connect
export interface GraphViewProps { export interface GraphViewProps {
elements: Elements; elements: Elements;
onElementClick: (element: Node | Edge) => void; onElementClick?: (element: Node | Edge) => void;
onElementsRemove: (elements: Elements) => void; onElementsRemove?: (elements: Elements) => void;
onNodeDragStart: (node: Node) => void; onNodeDragStart?: (node: Node) => void;
onNodeDragStop: (node: Node) => void; onNodeDragStop?: (node: Node) => void;
onConnect: (connection: Connection | Edge) => void; onConnect?: (connection: Connection | Edge) => void;
onLoad: OnLoadFunc; onLoad?: OnLoadFunc;
onMove: () => void; onMove?: () => void;
selectionKeyCode: number; selectionKeyCode: number;
nodeTypes: NodeTypesType; nodeTypes: NodeTypesType;
edgeTypes: EdgeTypesType; edgeTypes: EdgeTypesType;
@@ -92,18 +92,21 @@ const GraphView = memo(
useEffect(() => { useEffect(() => {
updateDimensions(); updateDimensions();
setOnConnect(onConnect);
window.onresize = updateDimensions; window.onresize = updateDimensions;
if (onConnect) {
setOnConnect(onConnect);
}
return () => { return () => {
window.onresize = null; window.onresize = null;
}; };
}, []); }, []);
useD3Zoom(zoomPane, onMove, selectionKeyPressed); useD3Zoom({ zoomPane, onMove, selectionKeyPressed });
useEffect(() => { useEffect(() => {
if (state.d3Initialised) { if (state.d3Initialised && onLoad) {
onLoad({ onLoad({
fitView, fitView,
zoomIn, zoomIn,
@@ -111,7 +114,7 @@ const GraphView = memo(
project, project,
}); });
} }
}, [state.d3Initialised]); }, [state.d3Initialised, onLoad]);
useEffect(() => { useEffect(() => {
setSnapGrid({ snapToGrid, snapGrid }); setSnapGrid({ snapToGrid, snapGrid });
+4 -4
View File
@@ -6,9 +6,9 @@ import { Node, Transform, NodeTypesType, WrapNodeProps, Elements, Edge } from '.
interface NodeRendererProps { interface NodeRendererProps {
nodeTypes: NodeTypesType; nodeTypes: NodeTypesType;
onElementClick: (element: Node | Edge) => void; onElementClick?: (element: Node | Edge) => void;
onNodeDragStart: (node: Node) => void; onNodeDragStart?: (node: Node) => void;
onNodeDragStop: (node: Node) => void; onNodeDragStop?: (node: Node) => void;
onlyRenderVisibleNodes?: boolean; onlyRenderVisibleNodes?: boolean;
} }
@@ -22,7 +22,7 @@ function renderNode(
const nodeType = node.type || 'default'; const nodeType = node.type || 'default';
const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType<WrapNodeProps>; const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType<WrapNodeProps>;
if (!props.nodeTypes[nodeType]) { if (!props.nodeTypes[nodeType]) {
console.warn(`No node type found for type "${nodeType}". Using fallback type "default".`); console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`);
} }
const isSelected = selectedElements ? selectedElements.some(({ id }) => id === node.id) : false; const isSelected = selectedElements ? selectedElements.some(({ id }) => id === node.id) : false;
+8 -15
View File
@@ -26,14 +26,14 @@ import '../../style.css';
export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad'> { export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad'> {
elements: Elements; elements: Elements;
onElementClick: (element: Node | Edge) => void; onElementClick?: (element: Node | Edge) => void;
onElementsRemove: (elements: Elements) => void; onElementsRemove?: (elements: Elements) => void;
onNodeDragStart: (node: Node) => void; onNodeDragStart?: (node: Node) => void;
onNodeDragStop: (node: Node) => void; onNodeDragStop?: (node: Node) => void;
onConnect: (connection: Edge | Connection) => void; onConnect?: (connection: Edge | Connection) => void;
onLoad: OnLoadFunc; onLoad?: OnLoadFunc;
onMove: () => void; onMove?: () => void;
onSelectionChange: (elements: Elements | null) => void; onSelectionChange?: (elements: Elements | null) => void;
nodeTypes: NodeTypesType; nodeTypes: NodeTypesType;
edgeTypes: EdgeTypesType; edgeTypes: EdgeTypesType;
connectionLineType: string; connectionLineType: string;
@@ -106,13 +106,6 @@ const ReactFlow = ({
ReactFlow.displayName = 'ReactFlow'; ReactFlow.displayName = 'ReactFlow';
ReactFlow.defaultProps = { ReactFlow.defaultProps = {
onElementClick: () => {},
onElementsRemove: () => {},
onNodeDragStart: () => {},
onNodeDragStop: () => {},
onConnect: () => {},
onLoad: () => {},
onMove: () => {},
nodeTypes: { nodeTypes: {
input: InputNode, input: InputNode,
default: DefaultNode, default: DefaultNode,
+15 -7
View File
@@ -4,20 +4,26 @@ import { select, event } from 'd3-selection';
import { useStoreState, useStoreActions } from '../store/hooks'; import { useStoreState, useStoreActions } from '../store/hooks';
interface UseD3ZoomParams {
zoomPane: MutableRefObject<Element | null>;
selectionKeyPressed: boolean;
onMove?: () => void;
}
const d3ZoomInstance = d3Zoom const d3ZoomInstance = d3Zoom
.zoom() .zoom()
.scaleExtent([0.5, 2]) .scaleExtent([0.5, 2])
.filter(() => !event.button); .filter(() => !event.button);
export default (zoomPane: MutableRefObject<Element | null>, onMove: () => void, shiftPressed: boolean): void => { export default ({ zoomPane, onMove, selectionKeyPressed }: UseD3ZoomParams): void => {
const state = useStoreState(s => ({ const state = useStoreState((s) => ({
transform: s.transform, transform: s.transform,
d3Selection: s.d3Selection, d3Selection: s.d3Selection,
d3Zoom: s.d3Zoom, d3Zoom: s.d3Zoom,
})); }));
const initD3 = useStoreActions(actions => actions.initD3); const initD3 = useStoreActions((actions) => actions.initD3);
const updateTransform = useStoreActions(actions => actions.updateTransform); const updateTransform = useStoreActions((actions) => actions.updateTransform);
useEffect(() => { useEffect(() => {
if (zoomPane.current) { if (zoomPane.current) {
@@ -27,7 +33,7 @@ export default (zoomPane: MutableRefObject<Element | null>, onMove: () => void,
}, []); }, []);
useEffect(() => { useEffect(() => {
if (shiftPressed) { if (selectionKeyPressed) {
d3ZoomInstance.on('zoom', null); d3ZoomInstance.on('zoom', null);
} else { } else {
d3ZoomInstance.on('zoom', () => { d3ZoomInstance.on('zoom', () => {
@@ -37,7 +43,9 @@ export default (zoomPane: MutableRefObject<Element | null>, onMove: () => void,
updateTransform(event.transform); updateTransform(event.transform);
onMove(); if (onMove) {
onMove();
}
}); });
if (state.d3Selection && state.d3Zoom) { if (state.d3Selection && state.d3Zoom) {
@@ -53,5 +61,5 @@ export default (zoomPane: MutableRefObject<Element | null>, onMove: () => void,
return () => { return () => {
d3ZoomInstance.on('zoom', null); d3ZoomInstance.on('zoom', null);
}; };
}, [shiftPressed]); }, [selectionKeyPressed]);
}; };
+2 -2
View File
@@ -7,7 +7,7 @@ import { Elements, Node } from '../types';
interface HookParams { interface HookParams {
deleteKeyCode: number; deleteKeyCode: number;
onElementsRemove: (elements: Elements) => void; onElementsRemove?: (elements: Elements) => void;
} }
export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => { export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => {
@@ -19,7 +19,7 @@ export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => {
const deleteKeyPressed = useKeyPress(deleteKeyCode); const deleteKeyPressed = useKeyPress(deleteKeyCode);
useEffect(() => { useEffect(() => {
if (deleteKeyPressed && state.selectedElements) { if (onElementsRemove && deleteKeyPressed && state.selectedElements) {
let elementsToRemove = state.selectedElements; let elementsToRemove = state.selectedElements;
// we also want to remove the edges if only one node is selected // we also want to remove the edges if only one node is selected
+5 -4
View File
@@ -108,7 +108,8 @@ export interface NodeComponentProps {
yPos?: number; yPos?: number;
targetPosition?: Position; targetPosition?: Position;
sourcePosition?: Position; sourcePosition?: Position;
onClick?: (node: Node) => void | undefined; onClick?: (node: Node) => void;
onNodeDragStart?: (node: Node) => void;
onNodeDragStop?: (node: Node) => void; onNodeDragStop?: (node: Node) => void;
style?: CSSProperties; style?: CSSProperties;
} }
@@ -122,9 +123,9 @@ export interface WrapNodeProps {
xPos: number; xPos: number;
yPos: number; yPos: number;
isInteractive: boolean; isInteractive: boolean;
onClick: (node: Node) => void | undefined; onClick?: (node: Node) => void;
onNodeDragStart: (node: Node) => void; onNodeDragStart?: (node: Node) => void;
onNodeDragStop: (node: Node) => void; onNodeDragStop?: (node: Node) => void;
style?: CSSProperties; style?: CSSProperties;
sourcePosition?: Position; sourcePosition?: Position;
targetPosition?: Position; targetPosition?: Position;