feat(react): ssr
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { memo, useEffect, useState, type FC, type PropsWithChildren } from 'react';
|
||||
import { memo, type FC, type PropsWithChildren } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
@@ -37,18 +37,9 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
|
||||
position = 'bottom-left',
|
||||
}) => {
|
||||
const store = useStoreApi();
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||
const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector, shallow);
|
||||
const { zoomIn, zoomOut, fitView } = useReactFlow();
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(true);
|
||||
}, []);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onZoomInHandler = () => {
|
||||
zoomIn();
|
||||
onZoomIn?.();
|
||||
|
||||
@@ -52,6 +52,8 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
disableKeyboardA11y,
|
||||
ariaLabel,
|
||||
rfId,
|
||||
dimensionWidth,
|
||||
dimensionHeight,
|
||||
}: WrapNodeProps) => {
|
||||
const store = useStoreApi();
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
@@ -181,7 +183,9 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
zIndex,
|
||||
transform: `translate(${xPosOrigin}px,${yPosOrigin}px)`,
|
||||
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
||||
visibility: initialized ? 'visible' : 'visible',
|
||||
visibility: initialized ? 'visible' : 'hidden',
|
||||
width: dimensionWidth,
|
||||
height: dimensionHeight,
|
||||
...style,
|
||||
}}
|
||||
data-id={id}
|
||||
|
||||
@@ -6,11 +6,23 @@ import { Provider } from '../../contexts/RFStoreContext';
|
||||
import { createRFStore } from '../../store';
|
||||
import type { ReactFlowState, Node, Edge } from '../../types';
|
||||
|
||||
function ReactFlowProvider({ children, nodes, edges }: { children: ReactNode; nodes?: Node[]; edges?: Edge[] }) {
|
||||
function ReactFlowProvider({
|
||||
children,
|
||||
nodes,
|
||||
edges,
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
}) {
|
||||
const storeRef = useRef<UseBoundStoreWithEqualityFn<StoreApi<ReactFlowState>> | null>(null);
|
||||
|
||||
if (!storeRef.current) {
|
||||
storeRef.current = createRFStore({ nodes, edges });
|
||||
storeRef.current = createRFStore({ nodes, edges, width, height });
|
||||
}
|
||||
|
||||
return <Provider value={storeRef.current}>{children}</Provider>;
|
||||
|
||||
@@ -98,6 +98,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
height: node.height ?? 0,
|
||||
origin: node.origin || props.nodeOrigin,
|
||||
});
|
||||
const initialized = (!!node.width && !!node.height) || (!!node.dimensions?.width && !!node.dimensions?.height);
|
||||
|
||||
return (
|
||||
<NodeComponent
|
||||
@@ -105,6 +106,8 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
id={node.id}
|
||||
className={node.className}
|
||||
style={node.style}
|
||||
dimensionWidth={node.dimensions?.width}
|
||||
dimensionHeight={node.dimensions?.height}
|
||||
type={nodeType}
|
||||
data={node.data}
|
||||
sourcePosition={node.sourcePosition || Position.Bottom}
|
||||
@@ -131,7 +134,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
isParent={!!node[internalsSymbol]?.isParent}
|
||||
noDragClassName={props.noDragClassName}
|
||||
noPanClassName={props.noPanClassName}
|
||||
initialized={!!node.width && !!node.height}
|
||||
initialized={initialized}
|
||||
rfId={props.rfId}
|
||||
disableKeyboardA11y={props.disableKeyboardA11y}
|
||||
ariaLabel={node.ariaLabel}
|
||||
|
||||
@@ -4,7 +4,19 @@ import StoreContext from '../../contexts/RFStoreContext';
|
||||
import ReactFlowProvider from '../../components/ReactFlowProvider';
|
||||
import type { Node, Edge } from '../../types';
|
||||
|
||||
function Wrapper({ children, nodes, edges }: { children: ReactNode; nodes?: Node[]; edges?: Edge[] }) {
|
||||
function Wrapper({
|
||||
children,
|
||||
nodes,
|
||||
edges,
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
}) {
|
||||
const isWrapped = useContext(StoreContext);
|
||||
|
||||
if (isWrapped) {
|
||||
@@ -14,7 +26,7 @@ function Wrapper({ children, nodes, edges }: { children: ReactNode; nodes?: Node
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactFlowProvider nodes={nodes} edges={edges}>
|
||||
<ReactFlowProvider nodes={nodes} edges={edges} width={width} height={height}>
|
||||
{children}
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
|
||||
@@ -166,6 +166,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
nodeDragThreshold,
|
||||
viewport,
|
||||
onViewportChange,
|
||||
width,
|
||||
height,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
@@ -181,7 +183,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
data-testid="rf__wrapper"
|
||||
id={id}
|
||||
>
|
||||
<Wrapper nodes={nodes} edges={edges}>
|
||||
<Wrapper nodes={nodes} edges={edges} width={width} height={height}>
|
||||
<GraphView
|
||||
onInit={onInit}
|
||||
onNodeClick={onNodeClick}
|
||||
|
||||
@@ -24,10 +24,20 @@ import type {
|
||||
FitViewOptions,
|
||||
} from '../types';
|
||||
|
||||
const createRFStore = ({ nodes, edges }: { nodes?: Node[]; edges?: Edge[] }) =>
|
||||
const createRFStore = ({
|
||||
nodes,
|
||||
edges,
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
}) =>
|
||||
createWithEqualityFn<ReactFlowState>(
|
||||
(set, get) => ({
|
||||
...getInitialState({ nodes, edges }),
|
||||
...getInitialState({ nodes, edges, width, height }),
|
||||
setNodes: (nodes: Node[]) => {
|
||||
const { nodes: storeNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
||||
const nextNodes = updateNodes(nodes, storeNodes, { nodeOrigin, elevateNodesOnSelect });
|
||||
|
||||
@@ -1,15 +1,45 @@
|
||||
import { devWarn, infiniteExtent, ConnectionMode, updateNodes } from '@xyflow/system';
|
||||
import {
|
||||
devWarn,
|
||||
infiniteExtent,
|
||||
ConnectionMode,
|
||||
updateNodes,
|
||||
getRectOfNodes,
|
||||
getTransformForBounds,
|
||||
Transform,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { Edge, Node, ReactFlowStore } from '../types';
|
||||
|
||||
const getInitialState = ({ nodes = [], edges = [] }: { nodes?: Node[]; edges?: Edge[] }): ReactFlowStore => {
|
||||
const getInitialState = ({
|
||||
nodes = [],
|
||||
edges = [],
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
width?: number;
|
||||
height?: number;
|
||||
}): ReactFlowStore => {
|
||||
const nextNodes = updateNodes(nodes, [], { nodeOrigin: [0, 0], elevateNodesOnSelect: false });
|
||||
|
||||
let transform: Transform = [0, 0, 1];
|
||||
|
||||
if (width && height) {
|
||||
const nodesWithDimensions = nextNodes.map((node) => ({
|
||||
...node,
|
||||
width: node.dimensions?.width,
|
||||
height: node.dimensions?.height,
|
||||
}));
|
||||
const bounds = getRectOfNodes(nodesWithDimensions, [0, 0]);
|
||||
transform = getTransformForBounds(bounds, width, height, 0.5, 2, 0.1);
|
||||
}
|
||||
|
||||
return {
|
||||
rfId: '1',
|
||||
width: 0,
|
||||
height: 0,
|
||||
transform: [0, 0, 1],
|
||||
transform,
|
||||
nodes: nextNodes,
|
||||
edges: edges,
|
||||
onNodesChange: null,
|
||||
|
||||
@@ -151,6 +151,8 @@ export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
|
||||
onError?: OnError;
|
||||
isValidConnection?: IsValidConnection;
|
||||
nodeDragThreshold?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
export type ReactFlowRefType = HTMLDivElement;
|
||||
|
||||
@@ -40,4 +40,6 @@ export type WrapNodeProps<NodeData = any> = Pick<
|
||||
noPanClassName: string;
|
||||
rfId: string;
|
||||
disableKeyboardA11y: boolean;
|
||||
dimensionWidth?: number;
|
||||
dimensionHeight?: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user