Merge pull request #2962 from wbkd/feat/edges-updatable

Feat/edges updatable
This commit is contained in:
Moritz Klack
2023-03-27 16:56:17 +02:00
committed by GitHub
10 changed files with 91 additions and 28 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@reactflow/background': minor
'@reactflow/core': minor
'@reactflow/minimap': minor
---
edges: add updatable option
@@ -36,7 +36,7 @@ const initialNodes: Node[] = [
</> </>
), ),
}, },
position: { x: 100, y: 100 }, position: { x: 75, y: 0 },
}, },
{ {
id: '3', id: '3',
@@ -55,9 +55,46 @@ const initialNodes: Node[] = [
width: 180, width: 180,
}, },
}, },
{
id: '4',
data: {
label: (
<>
Node <strong>D</strong>
</>
),
},
position: { x: -75, y: 100 },
},
{
id: '5',
data: {
label: (
<>
Node <strong>E</strong>
</>
),
},
position: { x: 150, y: 100 },
},
{
id: '6',
data: {
label: (
<>
Node <strong>F</strong>
</>
),
},
position: { x: 150, y: 250 },
},
]; ];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2', label: 'This is a draggable edge' }]; const initialEdges: Edge[] = [
{ id: 'e1-3', source: '1', target: '3', label: 'This edge can only be updated from source', updatable: 'source' },
{ id: 'e2-4', source: '2', target: '4', label: 'This edge can only be updated from target', updatable: 'target' },
{ id: 'e5-6', source: '5', target: '6', label: 'This edge can be updated from both sides' },
];
const onInit = (reactFlowInstance: ReactFlowInstance) => reactFlowInstance.fitView(); const onInit = (reactFlowInstance: ReactFlowInstance) => reactFlowInstance.fitView();
const onEdgeUpdateStart = (_: ReactMouseEvent, edge: Edge, handleType: HandleType) => const onEdgeUpdateStart = (_: ReactMouseEvent, edge: Edge, handleType: HandleType) =>
+26 -22
View File
@@ -55,6 +55,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
rfId, rfId,
ariaLabel, ariaLabel,
isFocusable, isFocusable,
isUpdatable,
pathOptions, pathOptions,
interactionWidth, interactionWidth,
}: WrapEdgeProps): JSX.Element | null => { }: WrapEdgeProps): JSX.Element | null => {
@@ -138,7 +139,6 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
const onEdgeUpdaterMouseOut = () => setUpdateHover(false); const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
const inactive = !elementsSelectable && !onClick; const inactive = !elementsSelectable && !onClick;
const handleEdgeUpdate = typeof onEdgeUpdate !== 'undefined';
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
if (elementSelectionKeys.includes(event.key) && elementsSelectable) { if (elementSelectionKeys.includes(event.key) && elementsSelectable) {
@@ -205,28 +205,32 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
interactionWidth={interactionWidth} interactionWidth={interactionWidth}
/> />
)} )}
{handleEdgeUpdate && ( {isUpdatable && (
<> <>
<EdgeAnchor {(isUpdatable === 'source' || isUpdatable === true) && (
position={sourcePosition} <EdgeAnchor
centerX={sourceX} position={sourcePosition}
centerY={sourceY} centerX={sourceX}
radius={edgeUpdaterRadius} centerY={sourceY}
onMouseDown={onEdgeUpdaterSourceMouseDown} radius={edgeUpdaterRadius}
onMouseEnter={onEdgeUpdaterMouseEnter} onMouseDown={onEdgeUpdaterSourceMouseDown}
onMouseOut={onEdgeUpdaterMouseOut} onMouseEnter={onEdgeUpdaterMouseEnter}
type="source" onMouseOut={onEdgeUpdaterMouseOut}
/> type="source"
<EdgeAnchor />
position={targetPosition} )}
centerX={targetX} {(isUpdatable === 'target' || isUpdatable === true) && (
centerY={targetY} <EdgeAnchor
radius={edgeUpdaterRadius} position={targetPosition}
onMouseDown={onEdgeUpdaterTargetMouseDown} centerX={targetX}
onMouseEnter={onEdgeUpdaterMouseEnter} centerY={targetY}
onMouseOut={onEdgeUpdaterMouseOut} radius={edgeUpdaterRadius}
type="target" onMouseDown={onEdgeUpdaterTargetMouseDown}
/> onMouseEnter={onEdgeUpdaterMouseEnter}
onMouseOut={onEdgeUpdaterMouseOut}
type="target"
/>
)}
</> </>
)} )}
</g> </g>
@@ -20,6 +20,7 @@ type StoreUpdaterProps = Pick<
| 'nodesConnectable' | 'nodesConnectable'
| 'nodesFocusable' | 'nodesFocusable'
| 'edgesFocusable' | 'edgesFocusable'
| 'edgesUpdatable'
| 'minZoom' | 'minZoom'
| 'maxZoom' | 'maxZoom'
| 'nodeExtent' | 'nodeExtent'
@@ -98,6 +99,7 @@ const StoreUpdater = ({
nodesConnectable, nodesConnectable,
nodesFocusable, nodesFocusable,
edgesFocusable, edgesFocusable,
edgesUpdatable,
elevateNodesOnSelect, elevateNodesOnSelect,
minZoom, minZoom,
maxZoom, maxZoom,
@@ -162,6 +164,7 @@ const StoreUpdater = ({
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState); useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
useDirectStoreUpdater('nodesFocusable', nodesFocusable, store.setState); useDirectStoreUpdater('nodesFocusable', nodesFocusable, store.setState);
useDirectStoreUpdater('edgesFocusable', edgesFocusable, store.setState); useDirectStoreUpdater('edgesFocusable', edgesFocusable, store.setState);
useDirectStoreUpdater('edgesUpdatable', edgesUpdatable, store.setState);
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState); useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
useDirectStoreUpdater('elevateNodesOnSelect', elevateNodesOnSelect, store.setState); useDirectStoreUpdater('elevateNodesOnSelect', elevateNodesOnSelect, store.setState);
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState); useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
@@ -39,6 +39,7 @@ type EdgeRendererProps = Pick<
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
nodesConnectable: s.nodesConnectable, nodesConnectable: s.nodesConnectable,
edgesFocusable: s.edgesFocusable, edgesFocusable: s.edgesFocusable,
edgesUpdatable: s.edgesUpdatable,
elementsSelectable: s.elementsSelectable, elementsSelectable: s.elementsSelectable,
width: s.width, width: s.width,
height: s.height, height: s.height,
@@ -66,10 +67,8 @@ const EdgeRenderer = ({
onEdgeUpdateEnd, onEdgeUpdateEnd,
children, children,
}: EdgeRendererProps) => { }: EdgeRendererProps) => {
const { edgesFocusable, elementsSelectable, width, height, connectionMode, nodeInternals, onError } = useStore( const { edgesFocusable, edgesUpdatable, elementsSelectable, width, height, connectionMode, nodeInternals, onError } =
selector, useStore(selector, shallow);
shallow
);
const edgeTree = useVisibleEdges(onlyRenderVisibleElements, nodeInternals, elevateEdgesOnSelect); const edgeTree = useVisibleEdges(onlyRenderVisibleElements, nodeInternals, elevateEdgesOnSelect);
if (!width) { if (!width) {
@@ -114,6 +113,9 @@ const EdgeRenderer = ({
const sourcePosition = sourceHandle?.position || Position.Bottom; const sourcePosition = sourceHandle?.position || Position.Bottom;
const targetPosition = targetHandle?.position || Position.Top; const targetPosition = targetHandle?.position || Position.Top;
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined')); const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
const isUpdatable =
typeof onEdgeUpdate !== 'undefined' &&
(edge.updatable || (edgesUpdatable && typeof edge.updatable === 'undefined'));
if (!sourceHandle || !targetHandle) { if (!sourceHandle || !targetHandle) {
onError?.('008', errorMessages['error008'](sourceHandle, edge)); onError?.('008', errorMessages['error008'](sourceHandle, edge));
@@ -173,6 +175,7 @@ const EdgeRenderer = ({
rfId={rfId} rfId={rfId}
ariaLabel={edge.ariaLabel} ariaLabel={edge.ariaLabel}
isFocusable={isFocusable} isFocusable={isFocusable}
isUpdatable={isUpdatable}
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined} pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
interactionWidth={edge.interactionWidth} interactionWidth={edge.interactionWidth}
/> />
@@ -115,6 +115,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
nodesFocusable, nodesFocusable,
nodeOrigin = initNodeOrigin, nodeOrigin = initNodeOrigin,
edgesFocusable, edgesFocusable,
edgesUpdatable,
elementsSelectable, elementsSelectable,
defaultViewport = initDefaultViewport, defaultViewport = initDefaultViewport,
minZoom = 0.5, minZoom = 0.5,
@@ -266,6 +267,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
nodesConnectable={nodesConnectable} nodesConnectable={nodesConnectable}
nodesFocusable={nodesFocusable} nodesFocusable={nodesFocusable}
edgesFocusable={edgesFocusable} edgesFocusable={edgesFocusable}
edgesUpdatable={edgesUpdatable}
elementsSelectable={elementsSelectable} elementsSelectable={elementsSelectable}
elevateNodesOnSelect={elevateNodesOnSelect} elevateNodesOnSelect={elevateNodesOnSelect}
minZoom={minZoom} minZoom={minZoom}
+1
View File
@@ -46,6 +46,7 @@ const initialState: ReactFlowStore = {
nodesConnectable: true, nodesConnectable: true,
nodesFocusable: true, nodesFocusable: true,
edgesFocusable: true, edgesFocusable: true,
edgesUpdatable: true,
elementsSelectable: true, elementsSelectable: true,
elevateNodesOnSelect: true, elevateNodesOnSelect: true,
fitViewOnInit: false, fitViewOnInit: false,
@@ -112,6 +112,7 @@ export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
nodesFocusable?: boolean; nodesFocusable?: boolean;
nodeOrigin?: NodeOrigin; nodeOrigin?: NodeOrigin;
edgesFocusable?: boolean; edgesFocusable?: boolean;
edgesUpdatable?: boolean;
initNodeOrigin?: NodeOrigin; initNodeOrigin?: NodeOrigin;
elementsSelectable?: boolean; elementsSelectable?: boolean;
selectNodesOnDrag?: boolean; selectNodesOnDrag?: boolean;
+4
View File
@@ -36,8 +36,11 @@ type DefaultEdge<T = any> = {
ariaLabel?: string; ariaLabel?: string;
interactionWidth?: number; interactionWidth?: number;
focusable?: boolean; focusable?: boolean;
updatable?: EdgeUpdatable;
} & EdgeLabelOptions; } & EdgeLabelOptions;
export type EdgeUpdatable = boolean | HandleType;
export type SmoothStepPathOptions = { export type SmoothStepPathOptions = {
offset?: number; offset?: number;
borderRadius?: number; borderRadius?: number;
@@ -88,6 +91,7 @@ export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandl
onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void; onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void;
rfId?: string; rfId?: string;
isFocusable: boolean; isFocusable: boolean;
isUpdatable: EdgeUpdatable;
pathOptions?: BezierPathOptions | SmoothStepPathOptions; pathOptions?: BezierPathOptions | SmoothStepPathOptions;
}; };
+1
View File
@@ -182,6 +182,7 @@ export type ReactFlowStore = {
nodesConnectable: boolean; nodesConnectable: boolean;
nodesFocusable: boolean; nodesFocusable: boolean;
edgesFocusable: boolean; edgesFocusable: boolean;
edgesUpdatable: boolean;
elementsSelectable: boolean; elementsSelectable: boolean;
elevateNodesOnSelect: boolean; elevateNodesOnSelect: boolean;