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#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,
// ...truncated 589 lines <!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;
}