plouc / nivo

nivo provides a rich set of dataviz components, built on top of the awesome d3 and React libraries
https://nivo.rocks
MIT License
13.07k stars 1.02k forks source link

@nivo/sankey turn off node legend text and custom nodes #2624

Open shellyscheng opened 1 month ago

shellyscheng commented 1 month ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Is there a way to hide node labels on the bottom right corner?

Screenshot 2024-07-23 at 10 39 10 PM

Describe the solution you'd like A clear and concise description of what you want to happen.

I'm working on a chart which I set the nodeThickness to be 0, so I don't need the legend box at the bottom right corner. Would appreciate it if there's a way to turn it off.

Thanks a lot!

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context Add any other context or screenshots about the feature request here.

plouc commented 1 month ago

@shellyscheng, I think you posted before the upload of the screenshot could complete.

shellyscheng commented 1 month ago

@plouc opps! I just updated it! I figured it out by giving legends={[]}.

But now I have encountered two different questions for labels:

  1. How can I break labels into two lines?

For my use case, I hope the top label to have both source name and % of the total

Category 1
     x %
  1. How do we know the precise position of the node? Or customize node labels to be more complex, such as including images?

For my use case, I'm hoping to add two images at the center of the bottom two nodes

Thank you so much!

Screenshot 2024-07-24 at 1 29 10 AM

plouc commented 1 month ago

@shellyscheng, line breaks are not supported (SVG limitation), regarding customization, it's doable if you're familiar with SVG, while not exactly the same, you can have a look at this example: https://codesandbox.io/p/sandbox/sankey-custom-b4iyi?file=%2Fsrc%2FApp.js which uses a custom layer for the nodes, it could be a good start, but you'll have to figure out things by yourself as custom layers aren't documented.

shellyscheng commented 1 month ago

@plouc, that's super helpful! I have a follow-up question about using Nivo with custom layers. Once I added a custom layer for the nodes, the original labels set within the Nivo config got overwritten (pic 1 is Nivo label settings, pic 2 with CutomNodeLayer).

Is there a way to keep both the original labels and my custom elements? Like what I will for label 1 - 4 in the pic 1, target 1 and 2 below my headshot images in pic 2 if possible. Or is is possible to only use custom nodes for target 1 and 2 (with nodeThickness 100), not using custom nodefor label 1 -4 (with nodeThickness 0)

Additionally, the custom node layer seems to have a very fixed width restriction, which is not ideal for my labels. Would appreciate any insights that I could help improve the look! Thank you!

Here's my code for achieving pic 2:

const StyledImg = styled(Img)`
  width: 100px;
  max-width: 100%;
  position: relative;
  margin: 0 auto;
  display: flex;
  margin-top: 0.5rem;

const Label = styled.div`
  color: ${(props) => props.color};
  margin: 0 auto;
  wrap: nowrap;
`

const CustomNode = ({ node }) => {

  return (
    <g transform={`translate(${node.x0}, ${node.y0})`}>
      {node.id === "target 1" || node.id === "target 2" ? (
        <foreignObject width={node.width} height={node.height}>
          <div
            style={{
              width: node.width,
              height: node.height,
              boxSizing: "border-box",
              color: "black",
              textAlign: "left",
              padding: "9px",
              fontSize: "12px",
              backgroundColor: neutrals.white,
            }}
          >
            <StyledImg
              key={node.id}
              $imgSrc={imgUrl}
            />
          </div>
        </foreignObject>
      ) : (
        <foreignObject width={node.width + 100} height={node.height}>
          <Label
            style={{
              width: 21,
              height: 0,
              boxSizing: "border-box",
              backgroundColor: node.color,
              lineHeight: "1.5rem",
              fontSize: "18px",
              fontStretch: "75%",
              fontWeight: "700",
            }}
          >
            {node.id}
          </Label>
        </foreignObject>
      )}
    </g>
  )
}

Screenshot 2024-07-26 at 11 48 10 AM

Screenshot 2024-07-26 at 11 47 55 AM

plouc commented 1 month ago

@shellyscheng, you can pass the following layers: 'links' | 'nodes' | 'labels' | 'legends'. It's not possible to use a custom layer only for certain elements as it replaces all. As it's SVG, there are no line breaks, you could also use a custom layer for labels if you wish to customize them.

shellyscheng commented 1 month ago

Thank you, @plouc! I made it work now!