xyflow / xyflow

React Flow | Svelte Flow - Powerful open source libraries for building node-based UIs with React (https://reactflow.dev) or Svelte (https://svelteflow.dev). Ready out-of-the-box and infinitely customizable.
https://xyflow.com
MIT License
22.06k stars 1.46k forks source link

Add support for custom <Background /> variants. Allow for custom Logos as the background of the React Flow designer. #3380

Closed JerrodSmithFlex closed 1 month ago

JerrodSmithFlex commented 9 months ago

Please describe the feature that you want to propose

Support for custom variants that would allow developers to define the background type and pass in custom values. An example use case would be using a Logo for the Background dots in the designer.

moklick commented 9 months ago

We will check if it's possible to pass a custom pattern somehow.

Spreeyka commented 7 months ago

Any update in this topic? I need to add functionality with background as image.

bcakmakoglu commented 7 months ago

How about using a custom background component?

import { CSSProperties, memo, useRef } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';

import { useStore, type ReactFlowState, type BackgroundProps, BackgroundVariant } from 'reactflow';

const selector = (s: ReactFlowState) => ({ transform: s.transform, patternId: `pattern-${s.rfId}` });

function Background({
  id,
  gap = 150,
  size,
  offset = 2,
  color,
  style,
  className,
}: BackgroundProps) {
  const ref = useRef<SVGSVGElement>(null);
  const { transform, patternId } = useStore(selector, shallow);
  const patternSize = size || 1;
  const gapXY: [number, number] = Array.isArray(gap) ? gap : [gap, gap];
  const scaledGap: [number, number] = [gapXY[0] * transform[2] || 1, gapXY[1] * transform[2] || 1];
  const scaledSize = patternSize * transform[2];

  const patternOffset = [scaledSize / offset, scaledSize / offset]

  const _patternId = `${patternId}${id ? id : ''}`;

  return (
    <svg
      className={cc(['react-flow__background', className])}
      style={
        {
          ...style,
            position: 'absolute',
            width: '100%',
            height: '100%',
            top: 0,
            left: 0,
        } as CSSProperties
      }
      ref={ref}
      data-testid="rf__background"
    >
      <pattern
        id={_patternId}
        x={transform[0] % scaledGap[0]}
        y={transform[1] % scaledGap[1]}
        width={scaledGap[0]}
        height={scaledGap[1]}
        patternUnits="userSpaceOnUse"
        patternTransform={`translate(-${patternOffset[0]},-${patternOffset[1]})`}
      >
        <svg style={{
          transform: `scale(${transform[2]})`
        }} xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 221"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"/><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"/><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"/></svg>
      </pattern>
      <rect x="0" y="0" width="100%" height="100%" fill={`url(#${_patternId})`} />
    </svg>
  );
}

Background.displayName = 'Background';

export default memo(Background);

Here's an example.

moklick commented 1 month ago

We will not support custom components to be used as patterns. If you need anything custom, you can adjust the component as explained by @bcakmakoglu above.