refactor(react): minimap generic node type
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
|||||||
OnNodesChange,
|
OnNodesChange,
|
||||||
OnConnect,
|
OnConnect,
|
||||||
OnBeforeDelete,
|
OnBeforeDelete,
|
||||||
|
BuiltInNode,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
|
|
||||||
import ColorSelectorNode from './ColorSelectorNode';
|
import ColorSelectorNode from './ColorSelectorNode';
|
||||||
@@ -24,7 +25,7 @@ export type ColorSelectorNode = Node<
|
|||||||
{ color: string; onChange: (event: ChangeEvent<HTMLInputElement>) => void },
|
{ color: string; onChange: (event: ChangeEvent<HTMLInputElement>) => void },
|
||||||
'selectorNode'
|
'selectorNode'
|
||||||
>;
|
>;
|
||||||
export type MyNode = Node | ColorSelectorNode;
|
export type MyNode = BuiltInNode | ColorSelectorNode;
|
||||||
|
|
||||||
const onInit: OnInit<MyNode> = (reactFlowInstance) => {
|
const onInit: OnInit<MyNode> = (reactFlowInstance) => {
|
||||||
console.log('flow loaded:', reactFlowInstance);
|
console.log('flow loaded:', reactFlowInstance);
|
||||||
@@ -61,7 +62,7 @@ const CustomNodeFlow = () => {
|
|||||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
nds.map((node) => {
|
nds.map((node) => {
|
||||||
if (node.id !== '2') {
|
if (node.id !== '2' || node.type !== 'selectorNode') {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +165,7 @@ const CustomNodeFlow = () => {
|
|||||||
maxZoom={2}
|
maxZoom={2}
|
||||||
onBeforeDelete={onBeforeDelete}
|
onBeforeDelete={onBeforeDelete}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap<MyNode>
|
||||||
nodeStrokeColor={(n: MyNode): string => {
|
nodeStrokeColor={(n: MyNode): string => {
|
||||||
if (n.type === 'input') return '#0041d0';
|
if (n.type === 'input') return '#0041d0';
|
||||||
if (n.type === 'selectorNode') return bgColor;
|
if (n.type === 'selectorNode') return bgColor;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import UppercaseNode from './UppercaseNode';
|
|||||||
export type TextNode = Node<{ text: string }, 'text'>;
|
export type TextNode = Node<{ text: string }, 'text'>;
|
||||||
export type ResultNode = Node<{}, 'result'>;
|
export type ResultNode = Node<{}, 'result'>;
|
||||||
export type UppercaseNode = Node<{}, 'uppercase'>;
|
export type UppercaseNode = Node<{}, 'uppercase'>;
|
||||||
export type MyNode = Node | TextNode | ResultNode | UppercaseNode;
|
export type MyNode = TextNode | ResultNode | UppercaseNode;
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
text: TextNode,
|
text: TextNode,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { getNodesBounds, getBoundsOfRects, XYMinimap, type Rect, type XYMinimapI
|
|||||||
|
|
||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import { Panel } from '../../components/Panel';
|
import { Panel } from '../../components/Panel';
|
||||||
import type { ReactFlowState } from '../../types';
|
import type { ReactFlowState, Node } from '../../types';
|
||||||
|
|
||||||
import MiniMapNodes from './MiniMapNodes';
|
import MiniMapNodes from './MiniMapNodes';
|
||||||
import type { MiniMapProps } from './types';
|
import type { MiniMapProps } from './types';
|
||||||
@@ -38,7 +38,7 @@ const selector = (s: ReactFlowState) => {
|
|||||||
|
|
||||||
const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
|
const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
|
||||||
|
|
||||||
function MiniMapComponent({
|
function MiniMapComponent<NodeType extends Node = Node>({
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
nodeStrokeColor,
|
nodeStrokeColor,
|
||||||
@@ -61,7 +61,7 @@ function MiniMapComponent({
|
|||||||
inversePan,
|
inversePan,
|
||||||
zoomStep = 10,
|
zoomStep = 10,
|
||||||
offsetScale = 5,
|
offsetScale = 5,
|
||||||
}: MiniMapProps) {
|
}: MiniMapProps<NodeType>) {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const svg = useRef<SVGSVGElement>(null);
|
const svg = useRef<SVGSVGElement>(null);
|
||||||
const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector, shallow);
|
const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector, shallow);
|
||||||
@@ -119,7 +119,7 @@ function MiniMapComponent({
|
|||||||
|
|
||||||
const onSvgNodeClick = onNodeClick
|
const onSvgNodeClick = onNodeClick
|
||||||
? useCallback((event: MouseEvent, nodeId: string) => {
|
? useCallback((event: MouseEvent, nodeId: string) => {
|
||||||
const node = store.getState().nodeLookup.get(nodeId)!;
|
const node = store.getState().nodeLookup.get(nodeId)! as NodeType;
|
||||||
onNodeClick(event, node);
|
onNodeClick(event, node);
|
||||||
}, [])
|
}, [])
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -149,7 +149,7 @@ function MiniMapComponent({
|
|||||||
onClick={onSvgClick}
|
onClick={onSvgClick}
|
||||||
>
|
>
|
||||||
{ariaLabel && <title id={labelledBy}>{ariaLabel}</title>}
|
{ariaLabel && <title id={labelledBy}>{ariaLabel}</title>}
|
||||||
<MiniMapNodes
|
<MiniMapNodes<NodeType>
|
||||||
onClick={onSvgNodeClick}
|
onClick={onSvgNodeClick}
|
||||||
nodeColor={nodeColor}
|
nodeColor={nodeColor}
|
||||||
nodeStrokeColor={nodeStrokeColor}
|
nodeStrokeColor={nodeStrokeColor}
|
||||||
@@ -174,4 +174,4 @@ function MiniMapComponent({
|
|||||||
|
|
||||||
MiniMapComponent.displayName = 'MiniMap';
|
MiniMapComponent.displayName = 'MiniMap';
|
||||||
|
|
||||||
export const MiniMap = memo(MiniMapComponent);
|
export const MiniMap = memo(MiniMapComponent) as typeof MiniMapComponent;
|
||||||
|
|||||||
@@ -6,16 +6,17 @@ import { shallow } from 'zustand/shallow';
|
|||||||
|
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import { MiniMapNode } from './MiniMapNode';
|
import { MiniMapNode } from './MiniMapNode';
|
||||||
import type { ReactFlowState } from '../../types';
|
import type { ReactFlowState, Node } from '../../types';
|
||||||
import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types';
|
import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types';
|
||||||
|
|
||||||
declare const window: any;
|
declare const window: any;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
||||||
const selectorNodeIds = (s: ReactFlowState) => s.nodes.map((node) => node.id);
|
const selectorNodeIds = (s: ReactFlowState) => s.nodes.map((node) => node.id);
|
||||||
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
const getAttrFunction = <NodeType extends Node>(func: any): GetMiniMapNodeAttribute<NodeType> =>
|
||||||
|
func instanceof Function ? func : () => func;
|
||||||
|
|
||||||
function MiniMapNodes({
|
function MiniMapNodes<NodeType extends Node>({
|
||||||
nodeStrokeColor,
|
nodeStrokeColor,
|
||||||
nodeColor,
|
nodeColor,
|
||||||
nodeClassName = '',
|
nodeClassName = '',
|
||||||
@@ -25,12 +26,12 @@ function MiniMapNodes({
|
|||||||
// a component properly.
|
// a component properly.
|
||||||
nodeComponent: NodeComponent = MiniMapNode,
|
nodeComponent: NodeComponent = MiniMapNode,
|
||||||
onClick,
|
onClick,
|
||||||
}: MiniMapNodesProps) {
|
}: MiniMapNodesProps<NodeType>) {
|
||||||
const nodeIds = useStore(selectorNodeIds, shallow);
|
const nodeIds = useStore(selectorNodeIds, shallow);
|
||||||
const nodeOrigin = useStore(selector);
|
const nodeOrigin = useStore(selector);
|
||||||
const nodeColorFunc = getAttrFunction(nodeColor);
|
const nodeColorFunc = getAttrFunction<NodeType>(nodeColor);
|
||||||
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
const nodeStrokeColorFunc = getAttrFunction<NodeType>(nodeStrokeColor);
|
||||||
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
const nodeClassNameFunc = getAttrFunction<NodeType>(nodeClassName);
|
||||||
|
|
||||||
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ function MiniMapNodes({
|
|||||||
// minimize the cost of updates when individual nodes change.
|
// minimize the cost of updates when individual nodes change.
|
||||||
//
|
//
|
||||||
// For more details, see a similar commit in `NodeRenderer/index.tsx`.
|
// For more details, see a similar commit in `NodeRenderer/index.tsx`.
|
||||||
<NodeComponentWrapper
|
<NodeComponentWrapper<NodeType>
|
||||||
key={nodeId}
|
key={nodeId}
|
||||||
id={nodeId}
|
id={nodeId}
|
||||||
nodeOrigin={nodeOrigin}
|
nodeOrigin={nodeOrigin}
|
||||||
@@ -60,7 +61,7 @@ function MiniMapNodes({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeComponentWrapper = memo(function NodeComponentWrapper({
|
function NodeComponentWrapperInner<NodeType extends Node>({
|
||||||
id,
|
id,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeColorFunc,
|
nodeColorFunc,
|
||||||
@@ -74,9 +75,9 @@ const NodeComponentWrapper = memo(function NodeComponentWrapper({
|
|||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
nodeOrigin: NodeOrigin;
|
nodeOrigin: NodeOrigin;
|
||||||
nodeColorFunc: GetMiniMapNodeAttribute;
|
nodeColorFunc: GetMiniMapNodeAttribute<NodeType>;
|
||||||
nodeStrokeColorFunc: GetMiniMapNodeAttribute;
|
nodeStrokeColorFunc: GetMiniMapNodeAttribute<NodeType>;
|
||||||
nodeClassNameFunc: GetMiniMapNodeAttribute;
|
nodeClassNameFunc: GetMiniMapNodeAttribute<NodeType>;
|
||||||
nodeBorderRadius: number;
|
nodeBorderRadius: number;
|
||||||
nodeStrokeWidth?: number;
|
nodeStrokeWidth?: number;
|
||||||
NodeComponent: ComponentType<MiniMapNodeProps>;
|
NodeComponent: ComponentType<MiniMapNodeProps>;
|
||||||
@@ -84,7 +85,7 @@ const NodeComponentWrapper = memo(function NodeComponentWrapper({
|
|||||||
shapeRendering: string;
|
shapeRendering: string;
|
||||||
}) {
|
}) {
|
||||||
const { node, x, y } = useStore((s) => {
|
const { node, x, y } = useStore((s) => {
|
||||||
const node = s.nodeLookup.get(id);
|
const node = s.nodeLookup.get(id) as NodeType;
|
||||||
const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute;
|
const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -115,6 +116,8 @@ const NodeComponentWrapper = memo(function NodeComponentWrapper({
|
|||||||
id={node.id}
|
id={node.id}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
export default memo(MiniMapNodes);
|
const NodeComponentWrapper = memo(NodeComponentWrapperInner) as typeof NodeComponentWrapperInner;
|
||||||
|
|
||||||
|
export default memo(MiniMapNodes) as typeof MiniMapNodes;
|
||||||
|
|||||||
@@ -48,3 +48,5 @@ export type NodeWrapperProps<NodeType extends Node> = {
|
|||||||
nodeOrigin: NodeOrigin;
|
nodeOrigin: NodeOrigin;
|
||||||
onError?: OnError;
|
onError?: OnError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>;
|
||||||
|
|||||||
Reference in New Issue
Block a user