Search

Advanced Node Gallery

Styles

Create custom shaped nodes with multiple labels and styles.

Advanced Node Gallery
View live example →

Click on a node to see its styling data in the overlay.

For more advanced design needs, ReGraph lets you create nodes as rectangles or custom font icons, and style your nodes with multiple labels, custom interactions and interaction styling.

See also

import React, { useEffect, useRef, useState } from "react";
import { createRoot } from "react-dom/client";

import isEmpty from "lodash/isEmpty";
import { Chart } from "regraph";

import {
  data,
  interactionStylePresets,
  invertedInteractiveNode,
  normalInteractiveNode,
  positions,
} from "./data";

import "@fortawesome/fontawesome-free/css/fontawesome.css";
import "@fortawesome/fontawesome-free/css/solid.css";
import "@ci/theme/rg/css/properties.css";

function NodeGallery() {
  const [selection, setSelection] = useState({});
  const [items, setItems] = useState(data);
  const [interactiveNodesState, setInteractiveNodeState] = useState({});
  const [buttons, setButtons] = useState({
    labelButtons: {},
    buttonGroupLabels: {},
  });
  const [chartClasses, setChartClasses] = useState([]);
  const currentSubItem = useRef(null);
  const chartRef = useRef();

  const { labelButtons, buttonGroupLabels } = buttons;

  // register any required label as a button or a button group
  useEffect(() => {
    const buttonsToRegister = {};
    const buttonGroupsToRegister = {};

    function registerLabelButton(itemId, labelIndex, onItemInteractionLabelStyle, onClick) {
      buttonsToRegister[itemId] = buttonsToRegister[itemId] || {};
      buttonsToRegister[itemId][labelIndex] = { onClick };
      buttonsToRegister[itemId][onItemInteractionLabelStyle] = onItemInteractionLabelStyle;
    }

    function registerLabelButtonGroup(
      itemId,
      labelIndexArray,
      onItemInteractionLabelStyle,
      onClick
    ) {
      for (const labelIndex of labelIndexArray) {
        registerLabelButton(itemId, labelIndex, onItemInteractionLabelStyle, onClick);
        buttonGroupsToRegister[itemId] = {
          ...buttonGroupsToRegister[itemId],
          [labelIndex]: labelIndexArray,
        };
      }
    }

    function toggleLabelButtons(itemId, labelIndex, style) {
      // track which buttons are toggled
      const currentNodeInteractiveState = interactiveNodesState[itemId] ?? [];

      currentNodeInteractiveState[labelIndex] =
        currentNodeInteractiveState[labelIndex] === undefined
          ? true
          : !currentNodeInteractiveState[labelIndex];

      setInteractiveNodeState({
        ...interactiveNodesState,
        [itemId]: currentNodeInteractiveState,
      });

      // make sure all buttons are styled correctly depending on their toggle state
      const currentStyle = items[itemId];
      const originalStyle = data[itemId];

      const toggleStyle = { ...currentStyle, label: [...currentStyle.label] };
      toggleStyle.label[labelIndex] = {
        ...toggleStyle.label[labelIndex],
        ...style,
      };

      currentNodeInteractiveState.forEach((toggleValue, index) => {
        const originalLabelStyle = { ...originalStyle.label[index] };
        const labelStyle = toggleValue ? style : originalLabelStyle;
        toggleStyle.label[index] = {
          ...toggleStyle.label[index],
          ...labelStyle,
        };
      });

      setItems({ ...items, [itemId]: toggleStyle });
    }

    function toggleNodeStyle(itemId) {
      const interactiveNodeState = interactiveNodesState[itemId];

      const nodeStyle = !interactiveNodeState ? invertedInteractiveNode : normalInteractiveNode;
      setItems({ ...items, [itemId]: nodeStyle });

      const toggleInteractiveNodeState =
        interactiveNodesState[itemId] === undefined ? true : !interactiveNodesState[itemId];
      setInteractiveNodeState({ ...interactiveNodesState, [itemId]: toggleInteractiveNodeState });
    }

    registerLabelButton("t2", 2, interactionStylePresets.interactiveNodeHover, () => {
      toggleNodeStyle("t2");
    });

    registerLabelButtonGroup("t2", [3, 4], interactionStylePresets.interactiveNodeHover, () =>
      window.open("/docs#advanced-node-styling", "_blank")
    );

    registerLabelButton(
      "outside-buttons-rectangle",
      3,
      interactionStylePresets.outsideLabelActive,
      () => {
        toggleLabelButtons(
          "outside-buttons-rectangle",
          3,
          interactionStylePresets.outsideLabelHover
        );
      }
    );
    registerLabelButton(
      "outside-buttons-rectangle",
      5,
      interactionStylePresets.outsideLabelActive,
      () => {
        toggleLabelButtons(
          "outside-buttons-rectangle",
          5,
          interactionStylePresets.outsideLabelHover
        );
      }
    );
    registerLabelButton(
      "outside-buttons-rectangle",
      7,
      interactionStylePresets.outsideLabelActive,
      () => {
        toggleLabelButtons(
          "outside-buttons-rectangle",
          7,
          interactionStylePresets.outsideLabelHover
        );
      }
    );

    registerLabelButton(
      "outside-buttons-circle",
      3,
      interactionStylePresets.outsideLabelActive,
      () => {
        toggleLabelButtons("outside-buttons-circle", 3, interactionStylePresets.outsideLabelHover);
      }
    );
    registerLabelButton(
      "outside-buttons-circle",
      4,
      interactionStylePresets.outsideLabelActive,
      () => {
        toggleLabelButtons("outside-buttons-circle", 4, interactionStylePresets.outsideLabelHover);
      }
    );
    registerLabelButton(
      "outside-buttons-circle",
      5,
      interactionStylePresets.outsideLabelActive,
      () => {
        toggleLabelButtons("outside-buttons-circle", 5, interactionStylePresets.outsideLabelHover);
      }
    );

    setButtons((current) => ({
      ...current,
      labelButtons: buttonsToRegister,
      buttonGroupLabels: buttonGroupsToRegister,
    }));
  }, [data, positions, items, interactiveNodesState]);

  const handleChartClick = ({ id, subItem, preventDefault }) => {
    if (subItem !== null && subItem.type === "label") {
      const labelIndex = subItem.index;
      if (labelButtons[id] && labelButtons[id][labelIndex]) {
        labelButtons[id][labelIndex].onClick();
        preventDefault();
      }
    }
  };

  const handleChartChange = (change) => {
    if (change.selection) {
      setSelection({ ...change.selection });
    }
  };

  const handleItemInteraction = ({ id, subItem, setStyle, hovered, selected }) => {
    const style = {};

    if (selected) {
      style.fade = false;
    }

    const labelIndex = subItem !== null && subItem.type === "label" ? subItem.index : -1;
    const isHoverableLabel = id !== null && labelButtons[id] && labelButtons[id][labelIndex];
    const isAButtonGroup = isHoverableLabel && !!buttonGroupLabels?.[id]?.[labelIndex];
    const hoverStyle = isHoverableLabel && labelButtons[id][style];

    if (hovered && isHoverableLabel && !isAButtonGroup) {
      const label = [...items[id].label];
      label[labelIndex] = {
        ...label[labelIndex],
        ...hoverStyle,
      };
      style.label = label;
    } else if (hovered && isAButtonGroup) {
      const label = [...items[id].label];

      for (const index of buttonGroupLabels[id][labelIndex]) {
        label[index] = {
          ...label[index],
          ...hoverStyle,
        };
      }

      style.label = label;
    }

    // apply hover styling for buttons that are not toggled, otherwise only apply fade to selected nodes
    if (id === "t2" || (subItem && currentSubItem.current?.index !== subItem?.index)) {
      setStyle({
        [id]: style,
      });
    } else if (selected) {
      setStyle({ [id]: { fade: false } });
    }
    currentSubItem.current = subItem;
  };

  const handleHover = ({ id, subItem }) => {
    // On a hover interaction over a label button, append a class to the chart to style the cursor differently
    const labelIndex = subItem !== null && subItem.type === "label" ? subItem.index : -1;
    const isHoverableLabel = id !== null && labelButtons[id] && labelButtons[id][labelIndex];
    setChartClasses((current) =>
      isHoverableLabel
        ? [...current, "cursor-pointer"]
        : current.filter((className) => !className.startsWith("cursor-"))
    );

    if (id == null) {
      currentSubItem.current = null;
    }
  };

  return (
    <div style={{ width: "100%", height: "100%" }} className={chartClasses.join(" ")}>
      <Chart
        items={items}
        ref={chartRef}
        positions={positions}
        onClick={handleChartClick}
        onItemInteraction={handleItemInteraction}
        onChange={handleChartChange}
        onHover={handleHover}
        options={{
          navigation: false,
          overview: false,
          iconFontFamily: "Font Awesome 5 Free",
          selection: { color: "#FF9933" },
          hoverDelay: 0,
        }}
        animation={{ animate: true, time: 450 }}
      />
      {isEmpty(selection) ? (
        <pre className="properties empty">Select a node to view its properties.</pre>
      ) : (
        <pre className="properties" style={{ fontFamily: 'monospace, "Material Icons"' }}>
          {JSON.stringify(selection, null, 2)}
        </pre>
      )}
    </div>
  );
}

const FontReadyChart = React.lazy(() =>
  Promise.all([
    document.fonts.load("24px 'Raleway'"),
    document.fonts.load("24px 'Montserrat'"),
    document.fonts.load("24px 'Varela Round'"),
    document.fonts.load("24px 'Material Icons'", "air"),
    document.fonts.load("900 24px 'Font Awesome 5 Free'"),
  ]).then(() => ({
    default: NodeGallery,
  }))
);

export function Demo() {
  return (
    <React.Suspense fallback="">
      <FontReadyChart />
    </React.Suspense>
  );
}

const root = createRoot(document.getElementById("regraph"));
root.render(<Demo />);
export const interactionStylePresets = {
  outsideLabelActive: { backgroundColor: "#d1fae5" },
  outsideLabelHover: { backgroundColor: "#6ee7b7" },
  interactiveNodeHover: { backgroundColor: "#9A9B9B" },
};

const lineSVG = `data:image/svg+xml;base64,${btoa(
  `<svg xmlns="http://www.w3.org/2000/svg" fill="#e2e8f0" width="256px" height="5px">
  <g id="SVGRepo_bgCarrier" stroke-width="0">
  </g>
  <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round">
  </g>
  <g id="SVGRepo_iconCarrier">
    <rect width="256" height="5" />
  </g>
</svg>`
)}`;

function circleSVG(colour) {
  return `data:image/svg+xml;base64,${btoa(
    `<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg">
  <circle cx="128" cy="128" r="128" fill="${colour}"/>
</svg>`
  )}`;
}

function hexagonSVG(colour) {
  return `data:image/svg+xml;base64,${btoa(
    `<svg fill="${colour}" height="256px" width="256px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 184.751 184.751" xml:space="preserve">
    <g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="4.803526"></g><g id="SVGRepo_iconCarrier"> <path d="M0,92.375l46.188-80h92.378l46.185,80l-46.185,80H46.188L0,92.375z"></path> </g>
    </svg>
`
  )}`;
}

function squareSVG(colour) {
  return `data:image/svg+xml;base64,${btoa(
    `<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg">
  <rect width="256" height="256" fill="${colour}"/>
</svg>`
  )}`;
}

export const positions = {
  "t1-a": {
    x: -475,
    y: -45,
  },
  "t1-b": {
    x: -350,
    y: -45,
  },
  "t1-c": {
    x: -597,
    y: -45,
  },
  t2: {
    x: 425,
    y: 200,
  },
  "j1-rectangle": {
    x: -105,
    y: -361,
  },
  "j2-rectangle": {
    x: -105,
    y: -255,
  },
  "j3-rectangle": {
    x: -105,
    y: -140,
  },
  "j4-circle": {
    x: 247,
    y: 0,
  },
  "j5-circle": {
    x: 120,
    y: -22,
  },
  "j6-circle": {
    x: 203,
    y: -122,
  },
  "j7-profile-card": {
    x: -500,
    y: -300,
  },
  "c1-circle": {
    x: -40,
    y: 75,
  },
  "c1-hexagon": {
    x: -105,
    y: 200,
  },
  "c1-square": {
    x: 45,
    y: 200,
  },
  c1: {
    x: 380,
    y: -334,
  },
  "node_Emanuela Cooper": {
    x: 480,
    y: -150,
  },
  "node_Vito Morello": {
    x: 480,
    y: -45,
  },
  node_people: {
    x: 480,
    y: 45,
  },
  "outside-buttons-rectangle": {
    x: -597,
    y: 180,
  },
  "outside-buttons-circle": {
    x: -400,
    y: 180,
  },
};

function makeInteractiveNode(backgroundColour, accentColour) {
  return {
    type: "node",
    border: {
      color: accentColour,
      radius: [0, 10, 20, 10],
    },
    shape: {
      height: 85,
      width: 305,
    },
    color: backgroundColour,
    label: [
      {
        color: accentColour,
        backgroundColor: "transparent",
        fontSize: 35,
        position: {
          horizontal: "left",
          vertical: "middle",
        },
        fontIcon: {
          text: "fas fa-desktop",
        },
        margin: {
          left: 17.5,
        },
      },
      {
        bold: true,
        color: accentColour,
        text: "Interactive Node",
        backgroundColor: "transparent",
        fontSize: 16,
        padding: {
          top: 0,
          right: 10,
          bottom: 0,
          left: 0,
        },
        textAlignment: {
          horizontal: "left",
        },
        position: {
          horizontal: 70,
          vertical: 15,
        },
      },
      {
        bold: true,
        color: backgroundColour,
        text: "Invert Style",
        backgroundColor: accentColour,
        border: {
          radius: [10, 10, 10, 10],
        },
        margin: {
          top: 10,
          right: 0,
          bottom: 10,
          left: 0,
        },
        minWidth: 60,
        padding: {
          top: 8,
          right: 10,
          bottom: 6,
          left: 10,
        },
        position: {
          horizontal: 70,
        },
      },
      {
        bold: true,
        color: backgroundColour,
        backgroundColor: accentColour,
        fontSize: 13,
        margin: {
          top: 10,
          right: 10,
          bottom: 0,
          left: 10,
        },
        border: {
          radius: [10, 0, 0, 10],
        },
        padding: {
          top: 9,
          right: 10,
          bottom: 9.75,
          left: 10,
        },
        position: {
          vertical: "inherit",
        },
        fontIcon: {
          text: "fas fa-external-link-alt",
        },
      },
      {
        bold: true,
        color: backgroundColour,
        text: "Go to Docs ",
        backgroundColor: accentColour,
        border: {
          radius: [0, 10, 10, 0],
        },
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 30,
        },
        minWidth: 60,
        padding: {
          top: 8,
          right: 10,
          bottom: 6,
          left: 1,
        },
        position: {
          horizontal: 170,
          vertical: "inherit",
        },
      },
    ],
  };
}

export const invertedInteractiveNode = makeInteractiveNode("#3d405b", "#f4f3ee");

export const normalInteractiveNode = makeInteractiveNode("#f4f3ee", "#3d405b");

function makeProfileCardNode() {
  return {
    color: "#f1f5f9",
    border: {
      radius: [10, 10, 6, 6],
    },
    label: [
      {
        color: "rgb(81, 136, 168)",
        backgroundColor: "#f1f5f9",
        fontSize: 90,
        border: {
          color: "rgb(0, 85, 135)",
          width: 5,
          radius: [60, 60, 60, 60],
        },
        margin: {
          top: -50,
          right: 0,
          bottom: 0,
          left: 0,
        },
        minHeight: 100,
        minWidth: 100,
        position: {
          vertical: "top",
        },
        fontIcon: {
          text: "fas fa-user",
        },
      },
      {
        bold: true,
        color: "rgb(0, 85, 135)",
        text: "John Doe",
        backgroundColor: "transparent",
        fontSize: 30,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
      },
      {
        color: "rgb(81, 136, 168)",
        backgroundColor: "transparent",
        fontSize: 12,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
        fontIcon: {
          text: "fas fa-envelope",
        },
      },
      {
        color: "rgb(81, 136, 168)",
        text: "[email protected]",
        backgroundColor: "transparent",
        fontSize: 10,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          vertical: "inherit",
        },
      },
      {
        color: "rgb(81, 136, 168)",
        backgroundColor: "transparent",
        fontSize: 12,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
        fontIcon: {
          text: "fas fa-code",
        },
      },
      {
        color: "#334155",
        text: "Software Developer",
        backgroundColor: "transparent",
        fontSize: 10,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          vertical: "inherit",
        },
      },
      {
        color: "white",
        text: "[email protected]",
        backgroundColor: "rgb(81, 136, 168)",
        fontSize: 10,
        border: {
          radius: [0, 0, 6, 6],
        },
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
        minWidth: 300,
        padding: {
          top: 10,
          right: 0,
          bottom: 10,
          left: 0,
        },
        position: {
          vertical: "bottom",
        },
      },
    ],
    shape: {
      height: "auto",
      width: 300,
    },
  };
}

function makeBusinessProcessNode() {
  return {
    color: "#ecfdf5",
    border: {
      color: "#e5e5e5",
      width: 2,
      radius: [10, 10, 10, 10],
    },
    label: [
      {
        color: "#334155",
        text: "Business Process 1",
        backgroundColor: "transparent",
        fontSize: 26,
        margin: {
          top: 8,
          right: 0,
          bottom: 0,
          left: 8,
        },
        position: {
          horizontal: "left",
          vertical: "top",
        },
      },
      {
        color: "#334155",
        text: "2140.0",
        backgroundColor: "transparent",
        fontSize: 26,
        margin: {
          top: 0,
          right: 0,
          bottom: 20,
          left: 8,
        },
        textAlignment: {
          vertical: "bottom",
        },
        position: {
          horizontal: "left",
          vertical: "bottom",
        },
      },
      {
        color: "#334155",
        text: "GBP",
        backgroundColor: "transparent",
        fontSize: 20,
        margin: {
          top: 0,
          right: 0,
          bottom: 21,
          left: 0,
        },
        minHeight: 36,
        textAlignment: {
          vertical: "bottom",
        },
        position: {
          vertical: "bottom",
        },
      },
      {
        margin: {
          top: 0,
          right: 2,
          bottom: 15,
          left: 0,
        },
        fontSize: 36,
        backgroundColor: "transparent",
        minHeight: 36,
        textAlignment: {
          vertical: "bottom",
        },
        position: {
          horizontal: "right",
          vertical: "bottom",
        },
        fontIcon: {
          text: "fas fa-caret-up",
          color: "#10b981",
        },
      },
      {
        color: "#10b981",
        text: "55.12%",
        backgroundColor: "transparent",
        fontSize: 20,
        minHeight: 36,
        textAlignment: {
          vertical: "bottom",
        },
        position: {
          vertical: "inherit",
        },
        margin: {
          top: 0,
          right: 8,
          bottom: 20,
          left: 0,
        },
      },
      {
        border: {
          radius: [0, 0, 9, 9],
        },
        minHeight: 18,
        minWidth: 402,
        backgroundColor: "#e5e5e5",
        fontSize: 0,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: "center",
          vertical: 103,
        },
      },
      {
        border: {
          radius: [0, 0, 10, 10],
        },
        margin: {
          top: 1,
          right: 1,
          bottom: 1,
          left: 1,
        },
        fontSize: 10,
        backgroundColor: "#f1f5f9",
        minWidth: 394,
        position: {
          horizontal: "left",
          vertical: "bottom",
        },
      },
      {
        border: {
          radius: [0, 0, 0, 10],
        },
        margin: {
          top: 1,
          right: 1,
          bottom: 1,
          left: 1,
        },
        fontSize: 10,
        backgroundColor: "#10b981",
        minWidth: 230.5,
        position: {
          horizontal: "left",
          vertical: "bottom",
        },
      },
      {
        backgroundColor: "#f1f5f9",
        minWidth: 40,
        minHeight: 60,
        position: { vertical: "middle", horizontal: -46 },
        border: {
          radius: [5, 0, 0, 5],
          width: 2,
          color: "#e5e5e5",
        },
        fontSize: 26,
        fontIcon: {
          text: "fas fa-people-arrows",
          color: "#334155",
        },
      },
    ],
    shape: {
      height: 120,
      width: 400,
    },
  };
}

function makeShadowCircleNode(colour, icon, text) {
  return {
    type: "node",
    color: colour,
    border: {
      color: "#283618",
      width: 2,
    },
    size: 1.5,
    halos: [
      {
        color: "rgba(220,220,220,1)",
        radius: 29,
        width: 1,
      },
      {
        color: "rgba(220,220,220,0.8)",
        radius: 30,
        width: 1,
      },
      {
        color: "rgba(220,220,220,0.6)",
        radius: 31,
        width: 1,
      },
      {
        color: "rgba(220,220,220,0.4)",
        radius: 32,
        width: 1,
      },
      {
        color: "rgba(220,220,220,0.2)",
        radius: 33,
        width: 1,
      },
    ],
    label: [
      {
        color: "#283618",
        backgroundColor: "rgba(0, 0, 0, 0)",
        fontSize: 30,
        padding: {
          top: 6,
          right: 2,
          bottom: 0,
          left: 2,
        },
        position: {
          vertical: "top",
        },
        fontIcon: {
          text: icon,
          fontFamily: "Material Icons",
        },
      },
      {
        bold: true,
        color: "#283618",
        text: text,
        backgroundColor: "rgba(0, 0, 0, 0)",
        fontSize: 10,
        fontFamily: "Varela Round",
      },
    ],
  };
}

function makeMaterialIconLabel(icon, fontSize, colour) {
  return {
    position: {
      horizontal: "centre",
      vertical: "middle",
    },
    fontSize: fontSize,
    color: colour,
    backgroundColor: "transparent",
    fontIcon: {
      text: icon,
      fontFamily: "Material Icons",
    },
    padding: {
      top: 5,
      right: 2,
      bottom: 0,
      left: 2,
    },
  };
}

function makeCustomNodeLabel(image, size) {
  return {
    position: {
      horizontal: "centre",
      vertical: "middle",
    },
    backgroundColor: "transparent",
    image,
    padding: {
      top: 5,
      right: 2,
      bottom: 0,
      left: 2,
    },
    maxHeight: size,
    maxWidth: size,
  };
}

export const data = {
  t2: normalInteractiveNode,
  "t1-a": {
    color: "#A485D5",
    border: {
      color: "#69738C",
      width: 3,
      radius: [10, 10, 10, 10],
    },
    label: [
      {
        color: "white",
        backgroundColor: "transparent",
        fontSize: 50,
        position: {
          vertical: 24,
        },
        fontIcon: {
          text: "fas fa-server",
        },
      },
      {
        bold: true,
        color: "white",
        text: "Rack A",
        backgroundColor: "transparent",
        fontSize: 15,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
      },
      {
        color: "white",
        text: "OK",
        backgroundColor: "transparent",
        fontSize: 11,
        margin: {
          top: 5,
          right: 0,
          bottom: 0,
          left: 0,
        },
        padding: {
          top: 0,
          right: 20,
          bottom: 0,
          left: 20,
        },
      },
      {
        border: {
          radius: [0, 0, 8.5, 8.5],
        },
        backgroundColor: "#69738C",
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        minHeight: 21.5,
        minWidth: 103,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: -1.5,
          vertical: 130,
        },
      },
      {
        border: {
          radius: [0, 0, 0, 6.5],
        },
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        backgroundColor: "#FACA16",
        minHeight: 17,
        minWidth: 60,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: 0,
          vertical: 133,
        },
      },
    ],
    shape: {
      height: 150,
      width: 100,
    },
  },
  "t1-b": {
    color: "#A485D5",
    border: {
      color: "#69738C",
      width: 3,
      radius: [10, 10, 10, 10],
    },
    label: [
      {
        color: "white",
        backgroundColor: "transparent",
        fontSize: 50,
        position: {
          vertical: 24,
        },
        fontIcon: {
          text: "fas fa-server",
        },
      },
      {
        bold: true,
        color: "white",
        text: "Rack B",
        backgroundColor: "transparent",
        fontSize: 15,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
      },
      {
        color: "white",
        text: "High CPU usage",
        backgroundColor: "transparent",
        fontSize: 11,
        margin: {
          top: 5,
          right: 0,
          bottom: 0,
          left: 0,
        },
        padding: {
          top: 0,
          right: 20,
          bottom: 0,
          left: 20,
        },
      },
      {
        border: {
          radius: [0, 0, 8.5, 8.5],
        },
        backgroundColor: "#69738C",
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        minHeight: 21.5,
        minWidth: 103,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: -1.5,
          vertical: 130,
        },
      },
      {
        border: {
          radius: [0, 0, 0, 6.5],
        },
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        backgroundColor: "#FF3C38",
        minHeight: 17,
        minWidth: 80,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: 0,
          vertical: 133,
        },
      },
    ],
    shape: {
      height: 150,
      width: 100,
    },
  },
  "t1-c": {
    color: "#61A899",
    border: {
      color: "#69738C",
      width: 3,
      radius: [10, 10, 10, 10],
    },
    label: [
      {
        color: "white",
        backgroundColor: "transparent",
        fontSize: 50,
        position: {
          vertical: 24,
        },
        fontIcon: {
          text: "fas fa-hdd",
        },
      },
      {
        bold: true,
        color: "white",
        text: "Blade C",
        backgroundColor: "transparent",
        fontSize: 15,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 0,
        },
      },
      {
        color: "white",
        text: "OK",
        backgroundColor: "transparent",
        fontSize: 11,
        margin: {
          top: 5,
          right: 0,
          bottom: 0,
          left: 0,
        },
        padding: {
          top: 0,
          right: 20,
          bottom: 0,
          left: 20,
        },
      },
      {
        border: {
          radius: [0, 0, 8.5, 8.5],
        },
        backgroundColor: "#69738C",
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        minHeight: 21.5,
        minWidth: 103,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: -1.5,
          vertical: 130,
        },
      },
      {
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        border: {
          radius: [0, 0, 0, 6.5],
        },
        backgroundColor: "#65B540",
        minHeight: 17,
        minWidth: 30,
        padding: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
        position: {
          horizontal: 0,
          vertical: 133,
        },
      },
    ],
    shape: {
      height: 150,
      width: 100,
    },
  },
  "j1-rectangle": {
    color: "#34d399",
    label: [
      {
        color: "#34d399",
        backgroundColor: "#404347",
        fontSize: 35,
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 7.5,
        },
        minHeight: 55,
        padding: {
          top: 2,
          right: 2,
          bottom: 0,
          left: 10,
        },
        fontIcon: {
          text: "fas fa-random",
        },
      },
      {
        bold: true,
        color: "white",
        text: "Generate Data",
        fontFamily: "Montserrat",
        backgroundColor: "#404347",
        fontSize: "auto",
        margin: {
          top: 0,
          right: 7.5,
          bottom: 0,
          left: 0,
        },
        minHeight: 55,
        padding: {
          top: 2,
          right: 13,
          bottom: 0,
          left: 13,
        },
        position: {
          vertical: "inherit",
        },
      },
    ],
    shape: {
      height: 70,
      width: 230,
    },
  },
  "j2-rectangle": {
    color: "#fcd34d",
    label: [
      {
        color: "#fcd34d",
        backgroundColor: "#404347",
        fontSize: 35,
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 7.5,
        },
        minHeight: 55,
        padding: {
          top: 2,
          right: 2,
          bottom: 0,
          left: 10,
        },
        fontIcon: {
          text: "fas fa-database",
        },
      },
      {
        bold: true,
        color: "white",
        text: "Open Database",
        fontFamily: "Montserrat",
        backgroundColor: "#404347",
        fontSize: "auto",
        margin: {
          top: 0,
          right: 7.5,
          bottom: 0,
          left: 0,
        },
        minHeight: 55,
        padding: {
          top: 2,
          right: 10,
          bottom: 0,
          left: 10,
        },
        position: {
          vertical: "inherit",
        },
      },
      {
        color: "white",
        text: "Unavailable",
        backgroundColor: "#404347",
        fontSize: 12,
        border: {
          radius: [20, 20, 20, 20],
          width: 3,
          color: "#fcd34d",
        },
        margin: {
          top: 0,
          right: 0,
          bottom: -20,
          left: -60,
        },
        padding: {
          top: 8,
          right: 8,
          bottom: 6,
          left: 8,
        },
        position: "ne",
      },
    ],
    shape: {
      height: 70,
      width: 230,
    },
  },
  "j3-rectangle": {
    color: "#fb7185",
    label: [
      {
        color: "#fb7185",
        backgroundColor: "#404347",
        fontSize: 35,
        margin: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 7.5,
        },
        minHeight: 55,
        padding: {
          top: 2,
          right: 2,
          bottom: 0,
          left: 10,
        },
        fontIcon: {
          text: "fas fa-bug",
        },
      },
      {
        bold: true,
        color: "white",
        text: "Debug Console",
        fontFamily: "Montserrat",
        backgroundColor: "#404347",
        fontSize: "auto",
        margin: {
          top: 0,
          right: 7.5,
          bottom: 0,
          left: 0,
        },
        minHeight: 55,
        padding: {
          top: 2,
          right: 10,
          bottom: 0,
          left: 10,
        },
        position: {
          vertical: "inherit",
        },
      },
      {
        color: "white",
        text: "Unavailable",
        backgroundColor: "#404347",
        fontSize: 12,
        border: {
          radius: [20, 20, 20, 20],
          width: 3,
          color: "#fb7185",
        },
        margin: {
          top: 0,
          right: 0,
          bottom: -20,
          left: -60,
        },
        padding: {
          top: 8,
          right: 8,
          bottom: 6,
          left: 8,
        },
        position: "ne",
      },
    ],
    shape: {
      height: 70,
      width: 230,
    },
  },
  "j4-circle": makeShadowCircleNode("#fefae0", "local_florist", "Bloom"),
  "j5-circle": makeShadowCircleNode("#f4f3ee", "air", "Blow"),
  "j6-circle": makeShadowCircleNode("#cd4631", "local_fire_department", "Burn"),
  "j7-profile-card": makeProfileCardNode(),
  "c1-circle": {
    color: "rgba(0,0,0,0)",
    label: [
      makeCustomNodeLabel(circleSVG("#0f2e18"), 56),
      makeCustomNodeLabel(circleSVG("white"), 50),
      makeCustomNodeLabel(circleSVG("#0f2e18"), 45),
      makeCustomNodeLabel(circleSVG("white"), 40),
      makeCustomNodeLabel(circleSVG("#0f2e18"), 35),
      {
        ...makeMaterialIconLabel("flight", 30, "#FFF"),
        padding: { top: 0, right: 0, bottom: 0, left: 1 },
      },
    ],
    size: 1.7,
    shape: "circle",
  },
  "c1-hexagon": {
    color: "rgba(0,0,0,0)",
    label: [
      makeCustomNodeLabel(hexagonSVG("#001845"), 56 + 8),
      makeCustomNodeLabel(hexagonSVG("white"), 50 + 8),
      makeCustomNodeLabel(hexagonSVG("#001845"), 45 + 8),
      makeCustomNodeLabel(hexagonSVG("white"), 40 + 8),
      makeCustomNodeLabel(hexagonSVG("#001845"), 35 + 8),
      makeMaterialIconLabel("place", 30, "#FFF"),
    ],
    size: 1.7,
    shape: "box",
  },
  "c1-square": {
    color: "rgba(0,0,0,0)",
    label: [
      makeCustomNodeLabel(squareSVG("#540b0e"), 56),
      makeCustomNodeLabel(squareSVG("white"), 50),
      makeCustomNodeLabel(squareSVG("#540b0e"), 45),
      makeCustomNodeLabel(squareSVG("white"), 40),
      makeCustomNodeLabel(squareSVG("#540b0e"), 35),
      makeMaterialIconLabel("question_answer", 30, "#FFF"),
    ],
    size: 1.7,
    shape: "box",
  },
  c1: makeBusinessProcessNode(),
  "node_Emanuela Cooper": {
    color: "#734b6d",
    border: {
      color: "white",
      width: 1,
      radius: 10,
    },
    label: [
      {
        image: `/img/people/Person3.png`,
        position: {
          horizontal: "left",
        },
        margin: 10,
      },
      {
        text: "Emanuela Cooper",
        position: {
          vertical: "inherit",
        },
        margin: {
          right: 10,
        },
        backgroundColor: "transparent",
        color: "#faf5ff",
      },
    ],
    glyphs: [
      {
        position: "ne",
        fontIcon: {
          text: "fas fa-plus",
          color: "white",
        },
        backgroundColor: "transparent",
        color: "rgba(183, 92, 170, 1)",
        size: 1.2,
      },
      {
        fontIcon: {
          text: "fas fa-expand-arrows-alt",
          color: "white",
        },
        color: "#513BC4",
        backgroundColor: "transparent",
        position: "se",
        size: 1.2,
      },
    ],
    shape: {
      width: "auto",
      height: "auto",
    },
  },
  "node_Vito Morello": {
    color: "#734b6d",
    border: {
      color: "white",
      width: 1,
      radius: 10,
    },
    label: [
      {
        text: "Vito Morello",
        margin: {
          left: 10,
        },
        position: {
          horizontal: "left",
        },
        backgroundColor: "transparent",
        color: "#faf5ff",
      },
      {
        image: `/img/people/Person9.png`,
        position: {
          vertical: "inherit",
        },
        margin: {
          left: 10,
          right: 20,
        },
      },
    ],
    glyphs: [
      {
        position: "ne",
        fontIcon: {
          text: "fas fa-plus",
          color: "white",
        },
        backgroundColor: "transparent",
        color: "rgba(183, 92, 170, 1)",
        size: 1.2,
      },
      {
        fontIcon: {
          text: "fas fa-expand-arrows-alt",
          color: "white",
        },
        color: "#513BC4",
        backgroundColor: "transparent",
        position: "se",
        size: 1.2,
      },
    ],
    shape: {
      width: "auto",
      height: 35,
    },
  },
  node_people: {
    color: "#734b6d",
    border: {
      color: "white",
      width: 1,
      radius: 10,
    },
    label: [
      {
        text: "Team",
        margin: {
          left: 10,
        },
        position: {
          horizontal: "left",
        },
        backgroundColor: "transparent",
        color: "#faf5ff",
      },
      {
        image: `/img/people/Person2.png`,
        position: {
          vertical: "inherit",
        },
        maxWidth: 30,
        maxHeight: 30,
        margin: {
          left: 10,
        },
      },
      {
        image: `/img/people/Person13.png`,
        position: {
          vertical: "inherit",
        },
        maxWidth: 30,
        maxHeight: 30,
        margin: {
          left: -5,
        },
      },
      {
        image: `/img/people/Person8.png`,
        position: {
          vertical: "inherit",
        },
        maxWidth: 30,
        maxHeight: 30,
        margin: {
          left: -5,
        },
      },
      {
        image: `/img/people/Person5.png`,
        position: {
          vertical: "inherit",
        },
        maxWidth: 30,
        maxHeight: 30,
        margin: {
          left: -5,
        },
      },
      {
        fontIcon: { text: "fas fa-ellipsis-h", color: "#334155" },
        position: {
          vertical: "inherit",
        },
        fontSize: 20,
        minWidth: 30,
        minHeight: 29.5,
        border: { radius: 180 },
        backgroundColor: "#f8fafc",
        padding: [1, 0, 0, 0],
        margin: {
          left: -5,
          right: 10,
        },
      },
    ],
    shape: {
      width: "auto",
      height: 50,
    },
  },
  "outside-buttons-rectangle": {
    color: "#f3f4f6",
    shape: { width: 100, height: 130 },
    border: { radius: 10, width: 1, color: "rgba(229, 231, 235, 0.8)" },
    label: [
      {
        fontIcon: { text: "fas fa-file-invoice", color: "#0d9488" },
        backgroundColor: "rgba(0,0,0,0)",
        fontSize: 40,
        position: { horizontal: "center", vertical: "top" },
        margin: {
          top: 15,
          bottom: 10,
        },
      },
      {
        text: "Insurance Policy",
        backgroundColor: "rgba(0,0,0,0)",
        color: "#334155",
        textWrap: "normal",
      },
      {
        text: "id: 842 921",
        backgroundColor: "rgba(0,0,0,0)",
        color: "#64748b",
        textWrap: "normal",
        fontSize: 12,
        margin: { top: 10, bottom: 10 },
      },
      // side buttons
      {
        fontIcon: { text: "fas fa-check", color: "#0f766e" },
        backgroundColor: "#f3f4f6",
        fontSize: 18,
        minHeight: 30,
        minWidth: 30,
        position: { horizontal: 110, vertical: "top" },
        border: { radius: [5, 5, 0, 0] },
      },
      {
        maxHeight: 1,
        maxWidth: 34,
        backgroundColor: "red",
        image: lineSVG,
      },
      {
        fontIcon: { text: "fas fa-bell-slash", color: "#0f766e" },
        backgroundColor: "#f3f4f6",
        fontSize: 18,
        minHeight: 30,
        minWidth: 30,
      },
      {
        maxHeight: 1,
        maxWidth: 34,
        backgroundColor: "red",
        image: lineSVG,
      },
      {
        fontIcon: { text: "fas fa-eye-slash", color: "#0f766e" },
        backgroundColor: "#f3f4f6",
        fontSize: 18,
        minHeight: 30,
        minWidth: 30,
        border: { radius: [0, 0, 5, 5] },
      },
    ],
  },
  "outside-buttons-circle": {
    color: "#f3f4f6",
    shape: { width: 130, height: 130 },
    border: { radius: 180, width: 1, color: "rgba(229, 231, 235, 0.8)" },
    label: [
      {
        fontIcon: { text: "fas fa-file-invoice", color: "#0d9488" },
        backgroundColor: "rgba(0,0,0,0)",
        fontSize: 40,
        position: { horizontal: "center", vertical: "top" },
        margin: {
          top: 15,
          bottom: 10,
        },
      },
      {
        text: "Insurance Policy",
        backgroundColor: "rgba(0,0,0,0)",
        color: "#334155",
        textWrap: "normal",
      },
      {
        text: "id: 842 921",
        backgroundColor: "rgba(0,0,0,0)",
        color: "#64748b",
        textWrap: "normal",
        fontSize: 12,
        margin: { top: 10, bottom: 10 },
      },
      // side buttons
      {
        fontIcon: { text: "fas fa-check", color: "#0f766e" },
        backgroundColor: "#f3f4f6",
        fontSize: 18,
        minHeight: 32,
        minWidth: 32,
        position: { horizontal: 135, vertical: 10 },
        border: { radius: 180 },
        margin: 2.5,
        padding: 0,
      },
      {
        fontIcon: { text: "fas fa-bell-slash", color: "#0f766e" },
        backgroundColor: "#f3f4f6",
        fontSize: 18,
        minHeight: 32,
        minWidth: 32,
        border: { radius: 180 },
        margin: 2.5,
        padding: 0,
      },
      {
        fontIcon: { text: "fas fa-eye-slash", color: "#0f766e" },
        backgroundColor: "#f3f4f6",
        fontSize: 18,
        minHeight: 32,
        minWidth: 32,
        border: { radius: 180 },
        margin: 2.5,
        padding: 0,
      },
    ],
  },
};
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="regraph" style="height: 100vh"></div>
    <script type="module" src="./code.jsx"></script>
  </body>
</html>
/* Import for Google Fonts: Montserrat, Raleway and Varela Round */
@import url("https://fonts.googleapis.com/css2?family=Montserrat&family=Raleway&family=Varela+Round&display=swap");

/* Import for Google's Material Icons */
@import url("https://fonts.googleapis.com/icon?family=Material+Icons");

.cursor-pointer canvas {
  cursor: pointer !important;
}

pre {
  max-width: 60vh;
}

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.