feat(elements): multiselect

This commit is contained in:
moklick
2020-10-22 00:03:30 +02:00
parent 13a3fd46b1
commit 65a1b5e07a
7 changed files with 58 additions and 8 deletions
+2 -2
View File
@@ -33,7 +33,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
markerEndId,
isHidden,
}: WrapEdgeProps) => {
const setSelectedElements = useStoreActions((actions) => actions.setSelectedElements);
const addSelectedElements = useStoreActions((actions) => actions.addSelectedElements);
const inactive = !elementsSelectable && !onClick;
const edgeClasses = cc([
@@ -46,7 +46,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
const onEdgeClick = useCallback(
(event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
if (elementsSelectable) {
setSelectedElements({ id, source, target });
addSelectedElements({ id, source, target });
}
if (onClick) {
+4 -4
View File
@@ -38,7 +38,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
isDragging,
}: WrapNodeProps) => {
const updateNodeDimensions = useStoreActions((actions) => actions.updateNodeDimensions);
const setSelectedElements = useStoreActions((actions) => actions.setSelectedElements);
const addSelectedElements = useStoreActions((actions) => actions.addSelectedElements);
const updateNodePosDiff = useStoreActions((actions) => actions.updateNodePosDiff);
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
@@ -94,7 +94,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
if (!isDraggable) {
if (isSelectable) {
unsetNodesSelection();
setSelectedElements({ id: node.id, type: node.type } as Node);
addSelectedElements({ id: node.id, type: node.type } as Node);
}
onClick?.(event, node);
@@ -109,7 +109,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
if (selectNodesOnDrag && isSelectable) {
unsetNodesSelection();
setSelectedElements({ id: node.id, type: node.type } as Node);
addSelectedElements({ id: node.id, type: node.type } as Node);
}
},
[node, selectNodesOnDrag, isSelectable, onNodeDragStart]
@@ -134,7 +134,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
// Because of that we set dragging to true inside the onDrag handler and handle the click here
if (!isDragging) {
if (isSelectable && !selectNodesOnDrag) {
setSelectedElements({ id: node.id, type: node.type } as Node);
addSelectedElements({ id: node.id, type: node.type } as Node);
}
onClick?.(event as MouseEvent, node);
+6 -1
View File
@@ -36,6 +36,7 @@ const FlowRenderer = ({
onMoveStart,
onMoveEnd,
selectionKeyCode,
multiSelectionKeyCode,
zoomOnScroll,
zoomOnDoubleClick,
paneMoveable,
@@ -48,12 +49,15 @@ const FlowRenderer = ({
onSelectionContextMenu,
}: FlowRendererProps) => {
const zoomPane = useRef<HTMLDivElement>(null);
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
const resetSelectedElements = useStoreActions((actions) => actions.resetSelectedElements);
const nodesSelectionActive = useStoreState((state) => state.nodesSelectionActive);
const selectionKeyPressed = useKeyPress(selectionKeyCode);
useResizeHandler(zoomPane);
useGlobalKeyHandler({ onElementsRemove, deleteKeyCode });
useGlobalKeyHandler({ onElementsRemove, deleteKeyCode, multiSelectionKeyCode });
useD3Zoom({
zoomPane,
@@ -73,6 +77,7 @@ const FlowRenderer = ({
(event: MouseEvent) => {
onPaneClick?.(event);
unsetNodesSelection();
resetSelectedElements();
},
[onPaneClick]
);
+3
View File
@@ -55,6 +55,7 @@ export interface GraphViewProps {
connectionLineStyle?: CSSProperties;
connectionLineComponent?: ConnectionLineComponent;
deleteKeyCode: number;
multiSelectionKeyCode: number;
snapToGrid: boolean;
snapGrid: [number, number];
onlyRenderVisibleNodes: boolean;
@@ -96,6 +97,7 @@ const GraphView = ({
connectionLineStyle,
connectionLineComponent,
selectionKeyCode,
multiSelectionKeyCode,
onElementsRemove,
deleteKeyCode,
elements,
@@ -245,6 +247,7 @@ const GraphView = ({
onElementsRemove={onElementsRemove}
deleteKeyCode={deleteKeyCode}
selectionKeyCode={selectionKeyCode}
multiSelectionKeyCode={multiSelectionKeyCode}
onMove={onMove}
onMoveStart={onMoveStart}
onMoveEnd={onMoveEnd}
+3
View File
@@ -82,6 +82,7 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
connectionLineComponent?: ConnectionLineComponent;
deleteKeyCode?: number;
selectionKeyCode?: number;
multiSelectionKeyCode?: number;
snapToGrid?: boolean;
snapGrid?: [number, number];
onlyRenderVisibleNodes?: boolean;
@@ -132,6 +133,7 @@ const ReactFlow = ({
connectionLineComponent,
deleteKeyCode = 8,
selectionKeyCode = 16,
multiSelectionKeyCode = 91,
snapToGrid = false,
snapGrid = [15, 15],
onlyRenderVisibleNodes = true,
@@ -182,6 +184,7 @@ const ReactFlow = ({
selectionKeyCode={selectionKeyCode}
onElementsRemove={onElementsRemove}
deleteKeyCode={deleteKeyCode}
multiSelectionKeyCode={multiSelectionKeyCode}
elements={elements}
onConnect={onConnect}
onConnectStart={onConnectStart}
+11 -1
View File
@@ -7,15 +7,20 @@ import { Elements } from '../types';
interface HookParams {
deleteKeyCode: number;
multiSelectionKeyCode: number;
onElementsRemove?: (elements: Elements) => void;
}
export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => {
export default ({ deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: HookParams): void => {
const selectedElements = useStoreState((state) => state.selectedElements);
const edges = useStoreState((state) => state.edges);
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
const setMultiSelectionActive = useStoreActions((actions) => actions.setMultiSelectionActive);
const resetSelectedElements = useStoreActions((actions) => actions.resetSelectedElements);
const deleteKeyPressed = useKeyPress(deleteKeyCode);
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
useEffect(() => {
if (onElementsRemove && deleteKeyPressed && selectedElements) {
@@ -30,6 +35,11 @@ export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => {
onElementsRemove(elementsToRemove);
unsetNodesSelection();
resetSelectedElements();
}
}, [deleteKeyPressed]);
useEffect(() => {
setMultiSelectionActive(multiSelectionKeyPressed);
}, [multiSelectionKeyPressed])
};
+29
View File
@@ -82,6 +82,8 @@ export interface StoreModel {
nodesConnectable: boolean;
elementsSelectable: boolean;
multiSelectionActive: boolean;
reactFlowVersion: string;
onConnect?: OnConnectFunc;
@@ -104,8 +106,10 @@ export interface StoreModel {
setSelection: Action<StoreModel, boolean>;
unsetNodesSelection: Action<StoreModel>;
resetSelectedElements: Action<StoreModel>;
setSelectedElements: Action<StoreModel, Elements | Node | Edge>;
addSelectedElements: Thunk<StoreModel, Elements | Node | Edge>;
updateTransform: Action<StoreModel, TransformXYK>;
@@ -141,6 +145,8 @@ export interface StoreModel {
zoom: Thunk<StoreModel, number, any, StoreModel>;
zoomIn: Thunk<StoreModel>;
zoomOut: Thunk<StoreModel>;
setMultiSelectionActive: Action<StoreModel, boolean>;
}
export const storeModel: StoreModel = {
@@ -187,6 +193,8 @@ export const storeModel: StoreModel = {
nodesConnectable: true,
elementsSelectable: true,
multiSelectionActive: false,
reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-',
setOnConnect: action((state, onConnect) => {
@@ -332,6 +340,9 @@ export const storeModel: StoreModel = {
unsetNodesSelection: action((state) => {
state.nodesSelectionActive = false;
}),
resetSelectedElements: action((state) => {
state.selectedElements = null;
}),
@@ -343,6 +354,20 @@ export const storeModel: StoreModel = {
state.selectedElements = selectedElements;
}),
addSelectedElements: thunk((actions, elements, helpers) => {
const { multiSelectionActive, selectedElements } = helpers.getState();
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
if (multiSelectionActive) {
const nextElements = selectedElements ? [...selectedElements, ...selectedElementsArr] : selectedElementsArr;
actions.setSelectedElements(nextElements);
return;
}
actions.setSelectedElements(elements);
}),
updateTransform: action((state, transform) => {
state.transform[0] = transform.x;
state.transform[1] = transform.y;
@@ -509,6 +534,10 @@ export const storeModel: StoreModel = {
zoomOut: thunk((actions) => {
actions.zoom(-0.2);
}),
setMultiSelectionActive: action((state, isActive) => {
state.multiSelectionActive = isActive;
})
};
const nodeEnv: string = process.env.NODE_ENV as string;