refactor(controls): dont import store but use actions
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ReactFlow, { ReactFlowProvider, addEdge, removeElements } from 'react-flow-renderer';
|
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls } from 'react-flow-renderer';
|
||||||
|
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
|
|
||||||
@@ -31,7 +31,9 @@ const ProviderFlow = () => {
|
|||||||
onElementClick={onElementClick}
|
onElementClick={onElementClick}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onElementsRemove={onElementsRemove}
|
onElementsRemove={onElementsRemove}
|
||||||
/>
|
>
|
||||||
|
<Controls />
|
||||||
|
</ReactFlow>
|
||||||
</div>
|
</div>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
import { fitView, zoomIn, zoomOut } from '../../utils/graph';
|
|
||||||
import { useStoreState, useStoreActions } from '../../store/hooks';
|
import { useStoreState, useStoreActions } from '../../store/hooks';
|
||||||
|
|
||||||
import PlusIcon from '../../../assets/icons/plus.svg';
|
import PlusIcon from '../../../assets/icons/plus.svg';
|
||||||
@@ -20,6 +19,10 @@ interface ControlProps extends React.HTMLAttributes<HTMLDivElement> {
|
|||||||
|
|
||||||
const Controls = ({ style, showZoom = true, showFitView = true, showInteractive = true, className }: ControlProps) => {
|
const Controls = ({ style, showZoom = true, showFitView = true, showInteractive = true, className }: ControlProps) => {
|
||||||
const setInteractive = useStoreActions((actions) => actions.setInteractive);
|
const setInteractive = useStoreActions((actions) => actions.setInteractive);
|
||||||
|
const fitView = useStoreActions((actions) => actions.fitView);
|
||||||
|
const zoomIn = useStoreActions((actions) => actions.zoomIn);
|
||||||
|
const zoomOut = useStoreActions((actions) => actions.zoomOut);
|
||||||
|
|
||||||
const isInteractive = useStoreState((s) => s.isInteractive);
|
const isInteractive = useStoreState((s) => s.isInteractive);
|
||||||
const mapClasses = classnames('react-flow__controls', className);
|
const mapClasses = classnames('react-flow__controls', className);
|
||||||
|
|
||||||
@@ -27,16 +30,19 @@ const Controls = ({ style, showZoom = true, showFitView = true, showInteractive
|
|||||||
<div className={mapClasses} style={style}>
|
<div className={mapClasses} style={style}>
|
||||||
{showZoom && (
|
{showZoom && (
|
||||||
<>
|
<>
|
||||||
<div className="react-flow__controls-button react-flow__controls-zoomin" onClick={zoomIn}>
|
<div className="react-flow__controls-button react-flow__controls-zoomin" onClick={() => zoomIn()}>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="react-flow__controls-button react-flow__controls-zoomout" onClick={zoomOut}>
|
<div className="react-flow__controls-button react-flow__controls-zoomout" onClick={() => zoomOut()}>
|
||||||
<MinusIcon />
|
<MinusIcon />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{showFitView && (
|
{showFitView && (
|
||||||
<div className="react-flow__controls-button react-flow__controls-fitview" onClick={() => fitView()}>
|
<div
|
||||||
|
className="react-flow__controls-button react-flow__controls-fitview"
|
||||||
|
onClick={() => fitView({ padding: 0.1 })}
|
||||||
|
>
|
||||||
<FitviewIcon />
|
<FitviewIcon />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { memo, ComponentType, CSSProperties } from 'react';
|
import React, { memo, ComponentType, CSSProperties } from 'react';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
|
||||||
import store from '../../store';
|
import { useStoreActions } from '../../store/hooks';
|
||||||
import { ElementId, Edge, EdgeCompProps } from '../../types';
|
import { ElementId, Edge, EdgeCompProps } from '../../types';
|
||||||
|
|
||||||
interface EdgeWrapperProps {
|
interface EdgeWrapperProps {
|
||||||
@@ -38,6 +38,7 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
|||||||
className,
|
className,
|
||||||
...rest
|
...rest
|
||||||
}: EdgeWrapperProps) => {
|
}: EdgeWrapperProps) => {
|
||||||
|
const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
|
||||||
const edgeClasses = cx('react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated });
|
const edgeClasses = cx('react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated });
|
||||||
const edgeGroupStyle: CSSProperties = {
|
const edgeGroupStyle: CSSProperties = {
|
||||||
pointerEvents: isInteractive ? 'all' : 'none',
|
pointerEvents: isInteractive ? 'all' : 'none',
|
||||||
@@ -47,7 +48,7 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
store.dispatch.setSelectedElements({ id, source, target });
|
setSelectedElements({ id, source, target });
|
||||||
|
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
onClick({ id, source, target, type });
|
onClick({ id, source, target, type });
|
||||||
|
|||||||
+2
-10
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, MutableRefObject } from 'react';
|
import { useEffect, MutableRefObject } from 'react';
|
||||||
import { zoom, zoomIdentity } from 'd3-zoom';
|
import { zoom } from 'd3-zoom';
|
||||||
import { select, event } from 'd3-selection';
|
import { select, event } from 'd3-selection';
|
||||||
|
|
||||||
import { useStoreState, useStoreActions } from '../store/hooks';
|
import { useStoreState, useStoreActions } from '../store/hooks';
|
||||||
@@ -11,8 +11,6 @@ interface UseD3ZoomParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ({ zoomPane, onMove, selectionKeyPressed }: UseD3ZoomParams): void => {
|
export default ({ zoomPane, onMove, selectionKeyPressed }: UseD3ZoomParams): void => {
|
||||||
const transform = useStoreState((s) => s.transform);
|
|
||||||
const d3Selection = useStoreState((s) => s.d3Selection);
|
|
||||||
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
||||||
|
|
||||||
const initD3 = useStoreActions((actions) => actions.initD3);
|
const initD3 = useStoreActions((actions) => actions.initD3);
|
||||||
@@ -35,7 +33,7 @@ export default ({ zoomPane, onMove, selectionKeyPressed }: UseD3ZoomParams): voi
|
|||||||
d3Zoom.on('zoom', null);
|
d3Zoom.on('zoom', null);
|
||||||
} else {
|
} else {
|
||||||
d3Zoom.on('zoom', () => {
|
d3Zoom.on('zoom', () => {
|
||||||
if (event.sourceEvent && event.sourceEvent.target !== zoomPane.current) {
|
if (!event.sourceEvent || (event.sourceEvent && event.sourceEvent.target !== zoomPane.current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,12 +43,6 @@ export default ({ zoomPane, onMove, selectionKeyPressed }: UseD3ZoomParams): voi
|
|||||||
onMove();
|
onMove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (d3Selection && d3Zoom) {
|
|
||||||
// we need to restore the graph transform otherwise d3 zoom transform and graph transform are not synced
|
|
||||||
const graphTransform = zoomIdentity.translate(transform[0], transform[1]).scale(transform[2]);
|
|
||||||
d3Selection.call(d3Zoom.transform, graphTransform);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
+52
-1
@@ -1,6 +1,7 @@
|
|||||||
import { createStore, Action, action } from 'easy-peasy';
|
import { createStore, Action, action, Thunk, thunk } from 'easy-peasy';
|
||||||
import isEqual from 'fast-deep-equal';
|
import isEqual from 'fast-deep-equal';
|
||||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||||
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
|
|
||||||
import { getDimensions } from '../utils';
|
import { getDimensions } from '../utils';
|
||||||
import { getHandleBounds } from '../components/Nodes/utils';
|
import { getHandleBounds } from '../components/Nodes/utils';
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
HandleType,
|
HandleType,
|
||||||
SetConnectionId,
|
SetConnectionId,
|
||||||
NodePosUpdate,
|
NodePosUpdate,
|
||||||
|
FitViewParams,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type TransformXYK = {
|
type TransformXYK = {
|
||||||
@@ -122,6 +124,11 @@ export interface StoreModel {
|
|||||||
setUserSelection: Action<StoreModel, XYPosition>;
|
setUserSelection: Action<StoreModel, XYPosition>;
|
||||||
updateUserSelection: Action<StoreModel, XYPosition>;
|
updateUserSelection: Action<StoreModel, XYPosition>;
|
||||||
unsetUserSelection: Action<StoreModel>;
|
unsetUserSelection: Action<StoreModel>;
|
||||||
|
|
||||||
|
fitView: Action<StoreModel, FitViewParams>;
|
||||||
|
zoom: Action<StoreModel, number>;
|
||||||
|
zoomIn: Thunk<StoreModel>;
|
||||||
|
zoomOut: Thunk<StoreModel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const storeModel: StoreModel = {
|
export const storeModel: StoreModel = {
|
||||||
@@ -370,6 +377,50 @@ export const storeModel: StoreModel = {
|
|||||||
setInteractive: action((state, isInteractive) => {
|
setInteractive: action((state, isInteractive) => {
|
||||||
state.isInteractive = isInteractive;
|
state.isInteractive = isInteractive;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
fitView: action((state, { padding = 0.1 }) => {
|
||||||
|
const { nodes, width, height, d3Selection, d3Zoom } = state;
|
||||||
|
|
||||||
|
if (!d3Selection || !d3Zoom || !nodes.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bounds = getRectOfNodes(nodes);
|
||||||
|
const maxBoundsSize = Math.max(bounds.width, bounds.height);
|
||||||
|
const k = Math.min(width, height) / (maxBoundsSize + maxBoundsSize * padding);
|
||||||
|
const boundsCenterX = bounds.x + bounds.width / 2;
|
||||||
|
const boundsCenterY = bounds.y + bounds.height / 2;
|
||||||
|
const transform = [width / 2 - boundsCenterX * k, height / 2 - boundsCenterY * k];
|
||||||
|
const fittedTransform = zoomIdentity.translate(transform[0], transform[1]).scale(k);
|
||||||
|
|
||||||
|
d3Selection.call(d3Zoom.transform, fittedTransform);
|
||||||
|
|
||||||
|
state.transform = [fittedTransform.x, fittedTransform.y, fittedTransform.k];
|
||||||
|
}),
|
||||||
|
|
||||||
|
zoom: action((state, amount) => {
|
||||||
|
const { d3Zoom, d3Selection, transform } = state;
|
||||||
|
const nextZoom = transform[2] + amount;
|
||||||
|
|
||||||
|
if (d3Zoom && d3Selection) {
|
||||||
|
d3Zoom.scaleTo(d3Selection, nextZoom);
|
||||||
|
|
||||||
|
const graphTransform = zoomIdentity.translate(transform[0], transform[1]).scale(nextZoom);
|
||||||
|
d3Selection.call(d3Zoom.transform, graphTransform);
|
||||||
|
|
||||||
|
console.log(graphTransform);
|
||||||
|
|
||||||
|
state.transform = [graphTransform.x, graphTransform.y, graphTransform.k];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
zoomIn: thunk((actions) => {
|
||||||
|
actions.zoom(0.2);
|
||||||
|
}),
|
||||||
|
|
||||||
|
zoomOut: thunk((actions) => {
|
||||||
|
actions.zoom(-0.2);
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const store = createStore(storeModel);
|
const store = createStore(storeModel);
|
||||||
|
|||||||
+1
-1
@@ -134,7 +134,7 @@ export interface WrapNodeProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type FitViewParams = {
|
export type FitViewParams = {
|
||||||
padding: number;
|
padding?: number;
|
||||||
};
|
};
|
||||||
export type FitViewFunc = (fitViewOptions: FitViewParams) => void;
|
export type FitViewFunc = (fitViewOptions: FitViewParams) => void;
|
||||||
export type ProjectFunc = (position: XYPosition) => XYPosition;
|
export type ProjectFunc = (position: XYPosition) => XYPosition;
|
||||||
|
|||||||
+1
-1
@@ -182,7 +182,7 @@ export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fitView = ({ padding }: FitViewParams = { padding: 0.1 }): void => {
|
export const fitView = ({ padding = 0.1 }: FitViewParams = { padding: 0.1 }): void => {
|
||||||
const { nodes, width, height, d3Selection, d3Zoom } = store.getState();
|
const { nodes, width, height, d3Selection, d3Zoom } = store.getState();
|
||||||
|
|
||||||
if (!d3Selection || !d3Zoom || !nodes.length) {
|
if (!d3Selection || !d3Zoom || !nodes.length) {
|
||||||
|
|||||||
Reference in New Issue
Block a user