feat(props): add nodeClickDistance

This commit is contained in:
moklick
2024-07-15 15:28:27 +02:00
parent edf7a8eec0
commit da35b9d7fb
16 changed files with 49 additions and 2 deletions
@@ -39,6 +39,7 @@ export function NodeWrapper<NodeType extends Node>({
rfId, rfId,
nodeTypes, nodeTypes,
nodeExtent, nodeExtent,
nodeClickDistance,
onError, onError,
}: NodeWrapperProps<NodeType>) { }: NodeWrapperProps<NodeType>) {
const { node, internals, isParent } = useStore((s) => { const { node, internals, isParent } = useStore((s) => {
@@ -76,6 +77,7 @@ export function NodeWrapper<NodeType extends Node>({
handleSelector: node.dragHandle, handleSelector: node.dragHandle,
nodeId: id, nodeId: id,
isSelectable, isSelectable,
nodeClickDistance,
}); });
const moveSelectedNodes = useMoveSelectedNodes(); const moveSelectedNodes = useMoveSelectedNodes();
@@ -22,6 +22,7 @@ export type FlowRendererProps<NodeType extends Node = Node> = Omit<
| 'selectNodesOnDrag' | 'selectNodesOnDrag'
| 'defaultMarkerColor' | 'defaultMarkerColor'
| 'rfId' | 'rfId'
| 'nodeClickDistance'
> & { > & {
isControlledViewport: boolean; isControlledViewport: boolean;
children: ReactNode; children: ReactNode;
@@ -33,6 +33,7 @@ export type GraphViewProps<NodeType extends Node = Node, EdgeType extends Edge =
| 'defaultViewport' | 'defaultViewport'
| 'disableKeyboardA11y' | 'disableKeyboardA11y'
| 'paneClickDistance' | 'paneClickDistance'
| 'nodeClickDistance'
> >
> & { > & {
rfId: string; rfId: string;
@@ -86,6 +87,7 @@ function GraphViewComponent<NodeType extends Node = Node, EdgeType extends Edge
onPaneScroll, onPaneScroll,
onPaneContextMenu, onPaneContextMenu,
paneClickDistance, paneClickDistance,
nodeClickDistance,
onEdgeContextMenu, onEdgeContextMenu,
onEdgeMouseEnter, onEdgeMouseEnter,
onEdgeMouseMove, onEdgeMouseMove,
@@ -183,6 +185,7 @@ function GraphViewComponent<NodeType extends Node = Node, EdgeType extends Edge
onNodeMouseMove={onNodeMouseMove} onNodeMouseMove={onNodeMouseMove}
onNodeMouseLeave={onNodeMouseLeave} onNodeMouseLeave={onNodeMouseLeave}
onNodeContextMenu={onNodeContextMenu} onNodeContextMenu={onNodeContextMenu}
nodeClickDistance={nodeClickDistance}
onlyRenderVisibleElements={onlyRenderVisibleElements} onlyRenderVisibleElements={onlyRenderVisibleElements}
noPanClassName={noPanClassName} noPanClassName={noPanClassName}
noDragClassName={noDragClassName} noDragClassName={noDragClassName}
@@ -24,6 +24,7 @@ export type NodeRendererProps<NodeType extends Node> = Pick<
| 'disableKeyboardA11y' | 'disableKeyboardA11y'
| 'nodeExtent' | 'nodeExtent'
| 'nodeTypes' | 'nodeTypes'
| 'nodeClickDistance'
>; >;
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
@@ -86,6 +87,7 @@ function NodeRendererComponent<NodeType extends Node>(props: NodeRendererProps<N
nodesConnectable={nodesConnectable} nodesConnectable={nodesConnectable}
nodesFocusable={nodesFocusable} nodesFocusable={nodesFocusable}
elementsSelectable={elementsSelectable} elementsSelectable={elementsSelectable}
nodeClickDistance={props.nodeClickDistance}
onError={onError} onError={onError}
/> />
); );
@@ -104,6 +104,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
onPaneScroll, onPaneScroll,
onPaneContextMenu, onPaneContextMenu,
paneClickDistance = 0, paneClickDistance = 0,
nodeClickDistance = 0,
children, children,
onReconnect, onReconnect,
onReconnectStart, onReconnectStart,
@@ -202,6 +203,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
onPaneScroll={onPaneScroll} onPaneScroll={onPaneScroll}
onPaneContextMenu={onPaneContextMenu} onPaneContextMenu={onPaneContextMenu}
paneClickDistance={paneClickDistance} paneClickDistance={paneClickDistance}
nodeClickDistance={nodeClickDistance}
onSelectionContextMenu={onSelectionContextMenu} onSelectionContextMenu={onSelectionContextMenu}
onSelectionStart={onSelectionStart} onSelectionStart={onSelectionStart}
onSelectionEnd={onSelectionEnd} onSelectionEnd={onSelectionEnd}
+3
View File
@@ -11,6 +11,7 @@ type UseDragParams = {
handleSelector?: string; handleSelector?: string;
nodeId?: string; nodeId?: string;
isSelectable?: boolean; isSelectable?: boolean;
nodeClickDistance?: number;
}; };
/** /**
@@ -25,6 +26,7 @@ export function useDrag({
handleSelector, handleSelector,
nodeId, nodeId,
isSelectable, isSelectable,
nodeClickDistance,
}: UseDragParams) { }: UseDragParams) {
const store = useStoreApi(); const store = useStoreApi();
const [dragging, setDragging] = useState<boolean>(false); const [dragging, setDragging] = useState<boolean>(false);
@@ -59,6 +61,7 @@ export function useDrag({
domNode: nodeRef.current, domNode: nodeRef.current,
isSelectable, isSelectable,
nodeId, nodeId,
nodeClickDistance,
}); });
return () => { return () => {
xyDrag.current?.destroy(); xyDrag.current?.destroy();
@@ -227,6 +227,10 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
* @default 0 * @default 0
*/ */
paneClickDistance?: number; paneClickDistance?: number;
/** Distance that the mouse can move between mousedown/up that will trigger a click
* @default 0
*/
nodeClickDistance?: number;
/** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */
onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>; onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>;
/** Custom node types to be available in a flow. /** Custom node types to be available in a flow.
+1
View File
@@ -53,6 +53,7 @@ export type NodeWrapperProps<NodeType extends Node> = {
nodeTypes?: NodeTypes; nodeTypes?: NodeTypes;
nodeExtent?: CoordinateExtent; nodeExtent?: CoordinateExtent;
onError?: OnError; onError?: OnError;
nodeClickDistance?: number;
}; };
export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>;
@@ -10,6 +10,7 @@ export type UseDragParams = {
handleSelector?: string; handleSelector?: string;
nodeId?: string; nodeId?: string;
isSelectable?: boolean; isSelectable?: boolean;
nodeClickDistance?: number;
onDrag?: OnDrag; onDrag?: OnDrag;
onDragStart?: OnDrag; onDragStart?: OnDrag;
onDragStop?: OnDrag; onDragStop?: OnDrag;
@@ -60,7 +61,8 @@ export default function drag(domNode: Element, params: UseDragParams) {
noDragClassName: params.noDragClass, noDragClassName: params.noDragClass,
handleSelector: params.handleSelector, handleSelector: params.handleSelector,
nodeId: params.nodeId, nodeId: params.nodeId,
isSelectable: params.isSelectable isSelectable: params.isSelectable,
nodeClickDistance: params.nodeClickDistance
}); });
} }
@@ -43,6 +43,7 @@
export let dragHandle: $$Props['dragHandle'] = undefined; export let dragHandle: $$Props['dragHandle'] = undefined;
export let initialized: $$Props['initialized'] = false; export let initialized: $$Props['initialized'] = false;
export let parentId: $$Props['parentId'] = undefined; export let parentId: $$Props['parentId'] = undefined;
export let nodeClickDistance: $$Props['nodeClickDistance'] = undefined;
let className: string = ''; let className: string = '';
export { className as class }; export { className as class };
@@ -156,6 +157,7 @@
disabled: false, disabled: false,
handleSelector: dragHandle, handleSelector: dragHandle,
noDragClass: 'nodrag', noDragClass: 'nodrag',
nodeClickDistance,
onNodeMouseDown: handleNodeSelection, onNodeMouseDown: handleNodeSelection,
onDrag: (event, _, targetNode, nodes) => { onDrag: (event, _, targetNode, nodes) => {
dispatchNodeEvent('nodedrag', { event, targetNode, nodes }); dispatchNodeEvent('nodedrag', { event, targetNode, nodes });
@@ -34,4 +34,5 @@ export type NodeWrapperProps = Pick<
zIndex: number; zIndex: number;
node: InternalNode; node: InternalNode;
initialized: boolean; initialized: boolean;
nodeClickDistance?: number;
}; };
@@ -4,6 +4,11 @@
import { NodeWrapper } from '$lib/components/NodeWrapper'; import { NodeWrapper } from '$lib/components/NodeWrapper';
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import type { NodeRendererProps } from './types';
type $$Props = NodeRendererProps;
export let nodeClickDistance: $$Props['nodeClickDistance'] = 0;
const { const {
visibleNodes, visibleNodes,
@@ -76,6 +81,7 @@
measuredHeight={node.measured.height} measuredHeight={node.measured.height}
parentId={node.parentId} parentId={node.parentId}
{resizeObserver} {resizeObserver}
{nodeClickDistance}
on:nodeclick on:nodeclick
on:nodemouseenter on:nodemouseenter
on:nodemousemove on:nodemousemove
@@ -0,0 +1,3 @@
export type NodeRendererProps = {
nodeClickDistance?: number;
};
@@ -80,6 +80,7 @@
export let oninit: $$Props['oninit'] = undefined; export let oninit: $$Props['oninit'] = undefined;
export let nodeOrigin: $$Props['nodeOrigin'] = undefined; export let nodeOrigin: $$Props['nodeOrigin'] = undefined;
export let paneClickDistance: $$Props['paneClickDistance'] = 0; export let paneClickDistance: $$Props['paneClickDistance'] = 0;
export let nodeClickDistance: $$Props['nodeClickDistance'] = 0;
export let defaultMarkerColor = '#b1b1b7'; export let defaultMarkerColor = '#b1b1b7';
@@ -254,6 +255,7 @@
<div class="svelte-flow__edgelabel-renderer" /> <div class="svelte-flow__edgelabel-renderer" />
<div class="svelte-flow__viewport-portal" /> <div class="svelte-flow__viewport-portal" />
<NodeRenderer <NodeRenderer
{nodeClickDistance}
on:nodeclick on:nodeclick
on:nodemouseenter on:nodemouseenter
on:nodemousemove on:nodemousemove
@@ -138,6 +138,10 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
* @default 0 * @default 0
*/ */
paneClickDistance?: number; paneClickDistance?: number;
/** Distance that the mouse can move between mousedown/up that will trigger a click
* @default 0
*/
nodeClickDistance?: number;
/** Minimum zoom level /** Minimum zoom level
* @default 0.5 * @default 0.5
*/ */
+10 -1
View File
@@ -84,6 +84,7 @@ export type DragUpdateParams = {
isSelectable?: boolean; isSelectable?: boolean;
nodeId?: string; nodeId?: string;
domNode: Element; domNode: Element;
nodeClickDistance?: number;
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -105,7 +106,14 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
let abortDrag = false; // prevents unintentional dragging on multitouch let abortDrag = false; // prevents unintentional dragging on multitouch
// public functions // public functions
function update({ noDragClassName, handleSelector, domNode, isSelectable, nodeId }: DragUpdateParams) { function update({
noDragClassName,
handleSelector,
domNode,
isSelectable,
nodeId,
nodeClickDistance = 0,
}: DragUpdateParams) {
d3Selection = select(domNode); d3Selection = select(domNode);
function updateNodes({ x, y }: XYPosition, dragEvent: MouseEvent | null) { function updateNodes({ x, y }: XYPosition, dragEvent: MouseEvent | null) {
const { const {
@@ -264,6 +272,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
} }
const d3DragInstance = drag() const d3DragInstance = drag()
.clickDistance(nodeClickDistance)
.on('start', (event: UseDragEvent) => { .on('start', (event: UseDragEvent) => {
const { domNode, nodeDragThreshold, transform, snapGrid, snapToGrid } = getStoreItems(); const { domNode, nodeDragThreshold, transform, snapGrid, snapToGrid } = getStoreItems();