refactor(react): minimap generic node type

This commit is contained in:
moklick
2024-01-26 09:57:19 +01:00
parent 112a552deb
commit cccfc21114
5 changed files with 31 additions and 25 deletions
@@ -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;
+2
View File
@@ -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'>;