Planet Example
A complete adaptation of examples/planet.json using Background, StackH, Text, Align, Distribute, Arrow, and Ref
import { Graphic } from "@modular-svg/react";
import { useState } from "react";
const planets = [
{ id: "mercury", label: "Mercury", r: 15, fill: "#EBE3CF" },
{ id: "venus", label: "Venus", r: 36, fill: "#DC933C" },
{ id: "earth", label: "Earth", r: 38, fill: "#179DD7" },
{ id: "mars", label: "Mars", r: 21, fill: "#F1CF8E" },
];
// The key on the circle becomes its stable id, so refs can target it
function Planet({ id, r, fill }: { id: string; r: number; fill: string }) {
return <circle key={id} r={r} fill={fill} stroke="black" stroke-width={3} />;
}
export function PlanetExample() {
const [target, setTarget] = useState("mercury");
const label = planets.find((p) => p.id === target)?.label ?? target;
return (
<div>
<label style={{ display: "block", marginBottom: "1rem" }}>
Label planet:
<select
aria-label="Label planet"
value={target}
onChange={(e) => setTarget(e.target.value)}
style={{ marginLeft: "0.5rem", padding: "4px 8px" }}
>
{planets.map((p) => (
<option key={p.id} value={p.id}>
{p.label}
</option>
))}
</select>
</label>
<Graphic
style={{
border: "1px solid #ddd",
borderRadius: "8px",
display: "inline-block",
}}
margin={20}
>
<group>
<background padding={10}>
<stackH key="planets" spacing={50} alignment="centerY">
{planets.map((p) => (
<Planet key={p.id} id={p.id} r={p.r} fill={p.fill} />
))}
</stackH>
</background>
<align key="alignLabel" axis="x" alignment="center">
<text key="label" fill="black">
{label}
</text>
<ref target={target} />
</align>
<distribute key="distVertical" axis="y" spacing={60}>
<ref target="label" />
<ref target={target} />
</distribute>
<arrow key="arrow1" stroke="black" stroke-width={2}>
<ref target="label" />
<ref target={target} />
</arrow>
</group>
</Graphic>
</div>
);
}
Interactive Bar Chart
Bars bottom-aligned in a stackH, with align, distribute, and an arrow pointing out the tallest bar - adjust scale and spacing
import { Graphic } from "@modular-svg/react";
import { useState } from "react";
const data = [
{ id: "mon", value: 30, fill: "#60a5fa" },
{ id: "tue", value: 55, fill: "#34d399" },
{ id: "wed", value: 80, fill: "#fbbf24" },
{ id: "thu", value: 45, fill: "#f87171" },
{ id: "fri", value: 65, fill: "#a78bfa" },
];
function Bar({
id,
height,
fill,
}: {
id: string;
height: number;
fill: string;
}) {
return (
<rect
key={id}
width={30}
height={height}
fill={fill}
stroke="#333"
stroke-width={1}
/>
);
}
export function BarChart() {
const [scale, setScale] = useState(1);
const [spacing, setSpacing] = useState(15);
const peak = data.reduce((a, b) => (b.value > a.value ? b : a));
return (
<div>
<div style={{ marginBottom: "1rem" }}>
<label style={{ display: "block", marginBottom: "0.5rem" }}>
Scale: {scale.toFixed(1)}x
<input
type="range"
min="0.5"
max="2"
step="0.1"
value={scale}
onChange={(e) => setScale(Number(e.target.value))}
style={{ marginLeft: "1rem", width: "200px" }}
/>
</label>
<label style={{ display: "block", marginBottom: "0.5rem" }}>
Spacing: {spacing}px
<input
type="range"
min="5"
max="40"
value={spacing}
onChange={(e) => setSpacing(Number(e.target.value))}
style={{ marginLeft: "1rem", width: "200px" }}
/>
</label>
</div>
<Graphic
style={{
border: "1px solid #ddd",
borderRadius: "8px",
display: "inline-block",
}}
margin={20}
>
<group>
<background
padding={15}
fill="#f9f9f9"
stroke="#ddd"
stroke-width={1}
>
<stackH key="bars" spacing={spacing} alignment="bottom">
{data.map((d) => (
<Bar
key={d.id}
id={d.id}
height={d.value * scale}
fill={d.fill}
/>
))}
</stackH>
</background>
<align key="alignPeak" axis="x" alignment="center">
<text key="peakLabel" fill="#333">
Peak
</text>
<ref target={peak.id} />
</align>
<distribute key="distPeak" axis="y" spacing={25}>
<ref target="peakLabel" />
<ref target={peak.id} />
</distribute>
<arrow key="arrowPeak" stroke="#333" stroke-width={1}>
<ref target="peakLabel" />
<ref target={peak.id} />
</arrow>
</group>
</Graphic>
</div>
);
}