Categories
Visx

Add Polygons into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add polygons into our React app.

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/gradient @visx/group @visx/responsive @visx/scale @visx/shape

to install the packages.

Add Polygons

To add the polygons, we write:

import React from "react";
import { Polygon } from "@visx/shape";
import { Group } from "@visx/group";
import { scaleBand } from "@visx/scale";
import { GradientPinkRed } from "@visx/gradient";

export const background = "#7f82e3";
const polygonSize = 25;
const defaultMargin = { top: 10, right: 10, bottom: 10, left: 10 };

const polygons = [
  {
    sides: 3,
    fill: "rgb(174, 238, 248)",
    rotate: 90
  },
  {
    sides: 4,
    fill: "lightgreen",
    rotate: 45
  },
  {
    sides: 6,
    fill: "rgb(229, 130, 255)",
    rotate: 0
  },
  {
    sides: 8,
    fill: "url(#polygon-pink)",
    rotate: 0
  }
];

const yScale = scaleBand({
  domain: polygons.map((p, i) => i),
  padding: 0.8
});

const Example = ({ width, height, margin = defaultMargin }: PolygonProps) => {
  yScale.rangeRound([0, height - margin.top - margin.bottom]);
  const centerX = (width - margin.left - margin.right) / 2;
  return (
    <svg width={width} height={height}>
      <rect width={width} height={height} fill={background} rx={14} />
      <GradientPinkRed id="polygon-pink" />
      {polygons.map((polygon, i) => (
        <Group
          key={`polygon-${i}`}
          top={(yScale(i) || 0) + polygonSize / 2}
          left={margin.left + centerX}
        >
          <Polygon
            sides={polygon.sides}
            size={polygonSize}
            fill={polygon.fill}
            rotate={polygon.rotate}
          />
        </Group>
      ))}
    </svg>
  );
};

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

The background variable has the background’s color.

polygonSize is the size of each polygon in pixels.

We create the polygons array with the properties of the polygons.

We set the number of sides with the sides .

fill is the background of the polygon.

rotate is the number of degrees to rotate it.

In the Example component, we add the polygons onto the screen with the polygons.map callback.

We add the Polygon component with the props to set the sides, fill, and rotation angle.

Conclusion

We can add polygons into our React app with the Visx library.

Categories
Visx

Add Tree Views into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add tree views into our React app.

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/gradient @visx/group @visx/hierarchy @visx/responsive @visx/shape

to install the packages.

Create the Tree View

We can create tree views with different link types, animations, and layouts by writing:

import React, { useState } from "react";
import { Group } from "@visx/group";
import { hierarchy, Tree } from "@visx/hierarchy";
import { LinearGradient } from "@visx/gradient";
import { pointRadial } from "d3-shape";
import {
  LinkHorizontal,
  LinkVertical,
  LinkRadial,
  LinkHorizontalStep,
  LinkVerticalStep,
  LinkRadialStep,
  LinkHorizontalCurve,
  LinkVerticalCurve,
  LinkRadialCurve,
  LinkHorizontalLine,
  LinkVerticalLine,
  LinkRadialLine
} from "@visx/shape";

const data = {
  name: "T",
  children: [
    {
      name: "A",
      children: [
        { name: "A1" },
        { name: "A2" },
        {
          name: "C",
          children: [
            {
              name: "C1"
            },
            {
              name: "D",
              children: [
                {
                  name: "D1"
                },
                {
                  name: "D2"
                },
                {
                  name: "D3"
                }
              ]
            }
          ]
        }
      ]
    },
    { name: "Z" },
    {
      name: "B",
      children: [{ name: "B1" }, { name: "B2" }]
    }
  ]
};

function useForceUpdate() {
  const [, setValue] = useState(0);
  return () => setValue((value) => value + 1);
}

const defaultMargin = { top: 30, left: 30, right: 30, bottom: 70 };

function LinkControls({
  layout,
  orientation,
  linkType,
  stepPercent,
  setLayout,
  setOrientation,
  setLinkType,
  setStepPercent
}) {
  return (
    <div>
      <label>layout:</label>&nbsp;
      <select
        onClick={(e) => e.stopPropagation()}
        onChange={(e) => setLayout(e.target.value)}
        value={layout}
      >
        <option value="cartesian">cartesian</option>
        <option value="polar">polar</option>
      </select>
      &nbsp;&nbsp;
      <label>orientation:</label>&nbsp;
      <select
        onClick={(e) => e.stopPropagation()}
        onChange={(e) => setOrientation(e.target.value)}
        value={orientation}
        disabled={layout === "polar"}
      >
        <option value="vertical">vertical</option>
        <option value="horizontal">horizontal</option>
      </select>
      &nbsp;&nbsp;
      <label>link:</label>&nbsp;
      <select
        onClick={(e) => e.stopPropagation()}
        onChange={(e) => setLinkType(e.target.value)}
        value={linkType}
      >
        <option value="diagonal">diagonal</option>
        <option value="step">step</option>
        <option value="curve">curve</option>
        <option value="line">line</option>
      </select>
      {linkType === "step" && layout !== "polar" && (
        <>
          &nbsp;&nbsp;
          <label>step:</label>&nbsp;
          <input
            onClick={(e) => e.stopPropagation()}
            type="range"
            min={0}
            max={1}
            step={0.1}
            onChange={(e) => setStepPercent(Number(e.target.value))}
            value={stepPercent}
            disabled={linkType !== "step" || layout === "polar"}
          />
        </>
      )}
    </div>
  );
}

function getLinkComponent({ layout, linkType, orientation }) {
  let LinkComponent;

  if (layout === "polar") {
    if (linkType === "step") {
      LinkComponent = LinkRadialStep;
    } else if (linkType === "curve") {
      LinkComponent = LinkRadialCurve;
    } else if (linkType === "line") {
      LinkComponent = LinkRadialLine;
    } else {
      LinkComponent = LinkRadial;
    }
  } else if (orientation === "vertical") {
    if (linkType === "step") {
      LinkComponent = LinkVerticalStep;
    } else if (linkType === "curve") {
      LinkComponent = LinkVerticalCurve;
    } else if (linkType === "line") {
      LinkComponent = LinkVerticalLine;
    } else {
      LinkComponent = LinkVertical;
    }
  } else if (linkType === "step") {
    LinkComponent = LinkHorizontalStep;
  } else if (linkType === "curve") {
    LinkComponent = LinkHorizontalCurve;
  } else if (linkType === "line") {
    LinkComponent = LinkHorizontalLine;
  } else {
    LinkComponent = LinkHorizontal;
  }
  return LinkComponent;
}

function Example({
  width: totalWidth,
  height: totalHeight,
  margin = defaultMargin
}) {
  const [layout, setLayout] = useState("cartesian");
  const [orientation, setOrientation] = useState("horizontal");
  const [linkType, setLinkType] = useState("diagonal");
  const [stepPercent, setStepPercent] = useState(0.5);
  const forceUpdate = useForceUpdate();

  const innerWidth = totalWidth - margin.left - margin.right;
  const innerHeight = totalHeight - margin.top - margin.bottom;

  let origin;
  let sizeWidth;
  let sizeHeight;

  if (layout === "polar") {
    origin = {
      x: innerWidth / 2,
      y: innerHeight / 2
    };
    sizeWidth = 2 * Math.PI;
    sizeHeight = Math.min(innerWidth, innerHeight) / 2;
  } else {
    origin = { x: 0, y: 0 };
    if (orientation === "vertical") {
      sizeWidth = innerWidth;
      sizeHeight = innerHeight;
    } else {
      sizeWidth = innerHeight;
      sizeHeight = innerWidth;
    }
  }

  const LinkComponent = getLinkComponent({ layout, linkType, orientation });

  return totalWidth < 10 ? null : (
    <div>
      <LinkControls
        layout={layout}
        orientation={orientation}
        linkType={linkType}
        stepPercent={stepPercent}
        setLayout={setLayout}
        setOrientation={setOrientation}
        setLinkType={setLinkType}
        setStepPercent={setStepPercent}
      />
      <svg width={totalWidth} height={totalHeight}>
        <LinearGradient id="links-gradient" from="#fd9b93" to="#fe6e9e" />
        <rect width={totalWidth} height={totalHeight} rx={14} fill="#272b4d" />
        <Group top={margin.top} left={margin.left}>
          <Tree
            root={hierarchy(data, (d) => (d.isExpanded ? null : d.children))}
            size={[sizeWidth, sizeHeight]}
            separation={(a, b) => (a.parent === b.parent ? 1 : 0.5) / a.depth}
          >
            {(tree) => (
              <Group top={origin.y} left={origin.x}>
                {tree.links().map((link, i) => (
                  <LinkComponent
                    key={i}
                    data={link}
                    percent={stepPercent}
                    stroke="rgb(254,110,158,0.6)"
                    strokeWidth="1"
                    fill="none"
                  />
                ))}

                {tree.descendants().map((node, key) => {
                  const width = 40;
                  const height = 20;

                  let top;
                  let left;
                  if (layout === "polar") {
                    const [radialX, radialY] = pointRadial(node.x, node.y);
                    top = radialY;
                    left = radialX;
                  } else if (orientation === "vertical") {
                    top = node.y;
                    left = node.x;
                  } else {
                    top = node.x;
                    left = node.y;
                  }

                  return (
                    <Group top={top} left={left} key={key}>
                      {node.depth === 0 && (
                        <circle
                          r={12}
                          fill="url('#links-gradient')"
                          onClick={() => {
                            node.data.isExpanded = !node.data.isExpanded;
                            console.log(node);
                            forceUpdate();
                          }}
                        />
                      )}
                      {node.depth !== 0 && (
                        <rect
                          height={height}
                          width={width}
                          y={-height / 2}
                          x={-width / 2}
                          fill="#272b4d"
                          stroke={node.data.children ? "#03c0dc" : "#26deb0"}
                          strokeWidth={1}
                          strokeDasharray={node.data.children ? "0" : "2,2"}
                          strokeOpacity={node.data.children ? 1 : 0.6}
                          rx={node.data.children ? 0 : 10}
                          onClick={() => {
                            node.data.isExpanded = !node.data.isExpanded;
                            forceUpdate();
                          }}
                        />
                      )}
                      <text
                        dy=".33em"
                        fontSize={9}
                        fontFamily="Arial"
                        textAnchor="middle"
                        style={{ pointerEvents: "none" }}
                        fill={
                          node.depth === 0
                            ? "#71248e"
                            : node.children
                            ? "white"
                            : "#26deb0"
                        }
                      >
                        {node.data.name}
                      </text>
                    </Group>
                  );
                })}
              </Group>
            )}
          </Tree>
        </Group>
      </svg>
    </div>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

We have the data object which we use to render the tree.

The useForceUpdate hook is used to return the function to force the tree to update when we change the layout, orientation, or link type.

Then we have the LinkControls component with the dropdowns to let us set the layout, orientation, or link type.

Next, we have the getLinkComponent function to return the component we render given the layoyt , linkType or orientation .

In the Example component, we get the layout, orientation, and linkType set in the LinkControls component.

The Group component contains all parts of the tree.

Then we add the tree view with the Tree component.

It has a render prop to with the tree parameter to get the links between parent and children with the tree.links() method, we render the links with the LinkComponent function component.

tree.descendants() returns the descendants.

We then render the nodes within the tree.descendants().map() callback.

We set the position of the nodes with the if statement.

Then we render circle s or rect s for the node depending on the depth level of the node.

In either case, we render the text inside the node, which is either inside the circle or rect .

Conclusion

We can render a tree view with cartesian or polar coordinates in our React app with the Visx library.

Categories
Visx

Add Gradients into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add gradients into our React app.

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/gradient @visx/responsive @visx/shape

to install the packages.

Create the Gradients

We can create gradients by writing:

import React from "react";
import { Bar } from "@visx/shape";
import {
  GradientDarkgreenGreen,
  GradientLightgreenGreen,
  GradientOrangeRed,
  GradientPinkBlue,
  GradientPinkRed,
  GradientPurpleOrange,
  GradientPurpleRed,
  GradientTealBlue,
  RadialGradient,
  LinearGradient
} from "@visx/gradient";

const defaultMargin = {
  top: 0,
  left: 0,
  right: 0,
  bottom: 0
};

const Gradients = [
  GradientPinkRed,
  ({ id }) => <RadialGradient id={id} from="#55bdd5" to="#4f3681" r="80%" />,
  GradientOrangeRed,
  GradientPinkBlue,
  ({ id }) => (
    <LinearGradient id={id} from="#351CAB" to="#621A61" rotate="-45" />
  ),
  GradientLightgreenGreen,
  GradientPurpleOrange,
  GradientTealBlue,
  GradientPurpleRed,
  GradientDarkgreenGreen
];

function Example({ width, height, margin = defaultMargin }) {
  const numColumns = width > 600 ? 5 : 2;
  const numRows = Gradients.length / numColumns;
  const columnWidth = Math.max(width / numColumns, 0);
  const rowHeight = Math.max((height - margin.bottom) / numRows, 0);

  return (
    <svg width={width} height={height}>
      {Gradients.map((Gradient, index) => {
        const columnIndex = index % numColumns;
        const rowIndex = Math.floor(index / numColumns);
        const id = `visx-gradient-demo-${index}-${rowIndex}${columnIndex}`;

        return (
          <React.Fragment key={id}>
            <Gradient id={id} />
            <Bar
              fill={`url(#${id})`}
              x={columnIndex * columnWidth}
              y={rowIndex * rowHeight}
              width={columnWidth}
              height={rowHeight}
              stroke="#ffffff"
              strokeWidth={8}
              rx={14}
            />
          </React.Fragment>
        );
      })}
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

We import the gradient components from the @visx/gradient module.

Then we put them in the Gradients array.

We can use the imported components directly.

Or we can create a function that returns the RadiantGradient to create the radiant gradient effect.

We can also use the LinearGradient component to create a linear gradient.

In the Example component, we call Gradients.map to render the components in the map callback.

We separate the gradients with a white bar by adding the Bar component.

Conclusion

We can add gradients into our React app with the Visx library.

Categories
Visx

Add a Dendrogram into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add a dendrogram into our React app.

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/gradient @visx/group @visx/hierarchy @visx/responsive @visx/shape

to install the packages.

Create the Dendrogram

We can create the dendrogram by writing:

import React, { useMemo } from "react";
import { Group } from "@visx/group";
import { Cluster, hierarchy } from "@visx/hierarchy";
import { LinkVertical } from "@visx/shape";
import { LinearGradient } from "@visx/gradient";

const citrus = "#ddf163";
const white = "#ffffff";
const green = "#79d259";
const aqua = "#37ac8c";
const merlinsbeard = "#f7f7f3";
const background = "#306c90";

const clusterData = {
  name: "$",
  children: [
    {
      name: "A",
      children: [
        { name: "A1" },
        { name: "A2" },
        {
          name: "C",
          children: [
            {
              name: "C1"
            }
          ]
        }
      ]
    },
    {
      name: "B",
      children: [{ name: "B1" }, { name: "B2" }]
    },
    {
      name: "X",
      children: [
        {
          name: "Z"
        }
      ]
    }
  ]
};

function Node({ node }) {
  const isRoot = node.depth === 0;
  const isParent = !!node.children;

  if (isRoot) return <RootNode node={node} />;

  return (
    <Group top={node.y} left={node.x}>
      {node.depth !== 0 && (
        <circle
          r={12}
          fill={background}
          stroke={isParent ? white : citrus}
          onClick={() => {
            alert(`clicked: ${JSON.stringify(node.data.name)}`);
          }}
        />
      )}
      <text
        dy=".33em"
        fontSize={9}
        fontFamily="Arial"
        textAnchor="middle"
        style={{ pointerEvents: "none" }}
        fill={isParent ? white : citrus}
      >
        {node.data.name}
      </text>
    </Group>
  );
}

function RootNode({ node }) {
  const width = 40;
  const height = 20;
  const centerX = -width / 2;
  const centerY = -height / 2;

  return (
    <Group top={node.y} left={node.x}>
      <rect
        width={width}
        height={height}
        y={centerY}
        x={centerX}
        fill="url('#top')"
      />
      <text
        dy=".33em"
        fontSize={9}
        fontFamily="Arial"
        textAnchor="middle"
        style={{ pointerEvents: "none" }}
        fill={background}
      >
        {node.data.name}
      </text>
    </Group>
  );
}

const defaultMargin = { top: 40, left: 0, right: 0, bottom: 40 };

function Example({ width, height, margin = defaultMargin }) {
  const data = useMemo(() => hierarchy(clusterData), []);
  const xMax = width - margin.left - margin.right;
  const yMax = height - margin.top - margin.bottom;

  return width < 10 ? null : (
    <svg width={width} height={height}>
      <LinearGradient id="top" from={green} to={aqua} />
      <rect width={width} height={height} rx={14} fill={background} />
      <Cluster root={data} size={[xMax, yMax]}>
        {(cluster) => (
          <Group top={margin.top} left={margin.left}>
            {cluster.links().map((link, i) => (
              <LinkVertical
                key={`cluster-link-${i}`}
                data={link}
                stroke={merlinsbeard}
                strokeWidth="1"
                strokeOpacity={0.2}
                fill="none"
              />
            ))}
            {cluster.descendants().map((node, i) => (
              <Node key={`cluster-node-${i}`} node={node} />
            ))}
          </Group>
        )}
      </Cluster>
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

We create the variables for the dendrogram at the top of the code.

The clusterData variable has the tree data for the dendrogram.

Next, we create the Node component to render the node of the dendrogram.

It has some text inside the Group component.

The root node is created with the RootBNode component.

It’s different from Node in that it has an extra rectangle surrounding the text.

The Example component is where the whole tree diagram comes together.

We render the tree nodes with the render prop of the Cluster component.

The data is set as the root prop’s value.

In the Group component of the render prop, we have the LinkVertical component to add the lines to link the parent and child nodes.

We get the child nodes wit the cluster.descendants method.

And we map them to Node s.

Conclusion

We can add a dendrogram into our React app with the Visx library.

Categories
Visx

Add Pie and Donut Charts into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add pie and donut charts into our React app

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/gradient @visx/group @visx/mock-data @visx/responsive @visx/scale @visx/shape

to install the packages.

Create the Pie and Donut Chart

We can create the pie and donut charts by writing:

import React, { useState } from "react";
import Pie from "@visx/shape/lib/shapes/Pie";
import { scaleOrdinal } from "@visx/scale";
import { Group } from "@visx/group";
import { GradientPinkBlue } from "@visx/gradient";
import letterFrequency from "@visx/mock-data/lib/mocks/letterFrequency";
import browserUsage from "@visx/mock-data/lib/mocks/browserUsage";
import { animated, useTransition, interpolate } from "react-spring";

const letters = letterFrequency.slice(0, 4);
const browserNames = Object.keys(browserUsage[0]).filter((k) => k !== "date");
const browsers = browserNames.map((name) => ({
  label: name,
  usage: Number(browserUsage[0][name])
}));

const usage = (d) => d.usage;
const frequency = (d) => d.frequency;
const getBrowserColor = scaleOrdinal({
  domain: browserNames,
  range: [
    "rgba(255,255,255,0.7)",
    "rgba(255,255,255,0.6)",
    "rgba(255,255,255,0.5)",
    "rgba(255,255,255,0.4)",
    "rgba(255,255,255,0.3)",
    "rgba(255,255,255,0.2)",
    "rgba(255,255,255,0.1)"
  ]
});
const getLetterFrequencyColor = scaleOrdinal({
  domain: letters.map((l) => l.letter),
  range: [
    "rgba(93,30,91,1)",
    "rgba(93,30,91,0.8)",
    "rgba(93,30,91,0.6)",
    "rgba(93,30,91,0.4)"
  ]
});

const defaultMargin = { top: 20, right: 20, bottom: 20, left: 20 };

function Example({ width, height, margin = defaultMargin, animate = true }) {
  const [selectedBrowser, setSelectedBrowser] = useState(null);
  const [selectedAlphabetLetter, setSelectedAlphabetLetter] = useState(null);

  if (width < 10) return null;

  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;
  const radius = Math.min(innerWidth, innerHeight) / 2;
  const centerY = innerHeight / 2;
  const centerX = innerWidth / 2;
  const donutThickness = 50;

  return (
    <svg width={width} height={height}>
      <GradientPinkBlue id="visx-pie-gradient" />
      <rect
        rx={14}
        width={width}
        height={height}
        fill="url('#visx-pie-gradient')"
      />
      <Group top={centerY + margin.top} left={centerX + margin.left}>
        <Pie
          data={
            selectedBrowser
              ? browsers.filter(({ label }) => label === selectedBrowser)
              : browsers
          }
          pieValue={usage}
          outerRadius={radius}
          innerRadius={radius - donutThickness}
          cornerRadius={3}
          padAngle={0.005}
        >
          {(pie) => (
            <AnimatedPie
              {...pie}
              animate={animate}
              getKey={(arc) => arc.data.label}
              onClickDatum={({ data: { label } }) =>
                animate &&
                setSelectedBrowser(
                  selectedBrowser && selectedBrowser === label ? null : label
                )
              }
              getColor={(arc) => getBrowserColor(arc.data.label)}
            />
          )}
        </Pie>
        <Pie
          data={
            selectedAlphabetLetter
              ? letters.filter(
                  ({ letter }) => letter === selectedAlphabetLetter
                )
              : letters
          }
          pieValue={frequency}
          pieSortValues={() => -1}
          outerRadius={radius - donutThickness * 1.3}
        >
          {(pie) => (
            <AnimatedPie
              {...pie}
              animate={animate}
              getKey={({ data: { letter } }) => letter}
              onClickDatum={({ data: { letter } }) =>
                animate &&
                setSelectedAlphabetLetter(
                  selectedAlphabetLetter && selectedAlphabetLetter === letter
                    ? null
                    : letter
                )
              }
              getColor={({ data: { letter } }) =>
                getLetterFrequencyColor(letter)
              }
            />
          )}
        </Pie>
      </Group>
    </svg>
  );
}

const fromLeaveTransition = ({ endAngle }) => ({
  startAngle: endAngle > Math.PI ? 2 * Math.PI : 0,
  endAngle: endAngle > Math.PI ? 2 * Math.PI : 0,
  opacity: 0
});

const enterUpdateTransition = ({ startAngle, endAngle }) => ({
  startAngle,
  endAngle,
  opacity: 1
});

function AnimatedPie({ animate, arcs, path, getKey, getColor, onClickDatum }) {
  const transitions = useTransition(arcs, getKey, {
    from: animate ? fromLeaveTransition : enterUpdateTransition,
    enter: enterUpdateTransition,
    update: enterUpdateTransition,
    leave: animate ? fromLeaveTransition : enterUpdateTransition
  });
  return (
    <>
      {transitions.map(({ item: arc, props, key }) => {
        const [centroidX, centroidY] = path.centroid(arc);
        const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1;

        return (
          <g key={key}>
            <animated.path
              d={interpolate(
                [props.startAngle, props.endAngle],
                (startAngle, endAngle) =>
                  path({
                    ...arc,
                    startAngle,
                    endAngle
                  })
              )}
              fill={getColor(arc)}
              onClick={() => onClickDatum(arc)}
              onTouchStart={() => onClickDatum(arc)}
            />
            {hasSpaceForLabel && (
              <animated.g style={{ opacity: props.opacity }}>
                <text
                  fill="white"
                  x={centroidX}
                  y={centroidY}
                  dy=".33em"
                  fontSize={9}
                  textAnchor="middle"
                  pointerEvents="none"
                >
                  {getKey(arc)}
                </text>
              </animated.g>
            )}
          </g>
        );
      })}
    </>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

We create the letters variable to add the data for the pie chart.

browerNames and browsers have the data for the donut chart.

usage and frequency are accessor functions for the data.

getBrowserData is a function that returns the browser donut chart colors.

getLetterFrequencyColor is a function that returns the browser pie chart colors.

defaultMargin have the margins for the charts.

The Example component is the main chart containers.

We have the selectedBrowser and selectedAlphabetLetter states to keep track of the select donut and pie segments respectively.

They’re selected when we click on them.

Next, we define the innerWidth , innerHeight n radius , centerY , centerX , and donutThickness variables to compute the dimensions for the charts.

In the return statement, we haver the GradientPinkBlue component with the background.

In the Group component, we have the Pie components.

They define the pie and donut charts.

The first Pie has the pie donut chart.

And the 2nd Pie is the pie chart inside the donut chart.

Each of them has a render prop that returns the AnimatedPie component to add animation for the pie and donut charts.

They have the onClickDatum prop which lets us set the selectedBrowser and selectedAlphabetLetter states respectively.

The getColor prop has a function that returns the color of the pie and donut segments.

The AnimatedPie component is created by rendering the animated.path to render the pie or donut.

We use the useTransition hook to add transition effects.

Conclusion

We can add pie and donut charts easily into our React app with the Visx library.