feat(react): ssr

This commit is contained in:
moklick
2023-10-05 17:28:05 +02:00
parent f37a2d9a19
commit 1acc59e44c
10 changed files with 90 additions and 22 deletions

View File

@@ -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?.();

View File

@@ -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}

View File

@@ -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>;

View File

@@ -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}

View File

@@ -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>
);

View File

@@ -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}

View File

@@ -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 });

View File

@@ -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,

View File

@@ -151,6 +151,8 @@ export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
onError?: OnError;
isValidConnection?: IsValidConnection;
nodeDragThreshold?: number;
width?: number;
height?: number;
};
export type ReactFlowRefType = HTMLDivElement;

View File

@@ -40,4 +40,6 @@ export type WrapNodeProps<NodeData = any> = Pick<
noPanClassName: string;
rfId: string;
disableKeyboardA11y: boolean;
dimensionWidth?: number;
dimensionHeight?: number;
};