@hemicycle/react
v0.1.5
Published
Core Hemicycle layout computations
Downloads
576
Maintainers
Readme
@hemicycle/react
React component for rendering parliament-style hemicycle seat charts.
@hemicycle/react is the React wrapper around @hemicycle/vanilla. Drop in a <Hemicycle> component, pass your seat data as props, and get a fully rendered SVG chart that re-renders reactively on changes.
Features
- Single
<Hemicycle>component — configure layout and data entirely through props - Per-seat React wrappers — wrap any seat with a custom React node (tooltips, popovers, links, etc.)
- Per-seat SVG props — attach
onClick,aria-*,data-*, and any other SVG attributes to individual seats - All
@hemicycle/vanillaoptions — shapes (arc,rect,circle), aisles, mirroring, radii, and more - TypeScript generics — type your seat data end-to-end with
HemicycleProps<T>
Installation
npm install @hemicycle/reactRequires React ≥ 18 and Node.js ≥ 18.
Quick Start
import { Hemicycle } from "@hemicycle/react";
export function ParliamentChart() {
return (
<Hemicycle
rows={7}
totalSeats={577}
innerRadius={40}
outerRadius={95}
width={800}
height={420}
/>
);
}API
<Hemicycle>
All props are optional. Accepts all @hemicycle/vanilla config options plus:
| Prop | Type | Description |
| ------------ | ------------------------------- | ------------------------------------------------- |
| data | HemicycleData<T>[] | Seat data array (see below) |
| svgProps | React.SVGProps<SVGSVGElement> | Extra props forwarded to the root <svg> element |
| seatConfig | SeatConfig<T> | Default visual style and behavior for all seats |
SeatConfig<T>
Extends the vanilla SeatConfig (shape, color, borderRadius, radius) with two React-specific additions:
| Option | Type | Description |
| --------- | -------------------------------- | ------------------------------------------------- |
| wrapper | (content, data) => ReactNode | Wraps each seat's <path> in a custom React node |
| props | React.SVGProps<SVGPathElement> | Default SVG props applied to every seat <path> |
Seat data
Each item in the data array identifies a seat by index or by coordinates, exactly as in @hemicycle/core, and may include a per-seat seatConfig to override styles and behavior for that seat:
type HemicycleData<T> = T &
({ idx: number } | { rowIndex: number; seatIndex: number }) & {
seatConfig?: SeatConfig<T>;
};Per-seat seatConfig is merged on top of the global seatConfig prop, so you only need to specify what differs.
Examples
Coloring seats by party
const members = [
{ idx: 0, party: "Left", color: "#e63946" },
{ idx: 1, party: "Center", color: "#457b9d" },
// ...
];
<Hemicycle
rows={7}
totalSeats={577}
data={members.map((m) => ({
idx: m.idx,
party: m.party,
seatConfig: { color: m.color },
}))}
/>;Clickable seats with tooltips
<Hemicycle
rows={5}
totalSeats={100}
data={seats.map((seat) => ({
idx: seat.idx,
seatConfig: {
color: seat.partyColor,
props: {
onClick: () => setSelected(seat),
style: { cursor: "pointer" },
},
wrapper: (content, data) => (
<Tooltip key={data?.idx} label={data?.party}>
{content}
</Tooltip>
),
},
}))}
/>Custom SVG container props
<Hemicycle
rows={5}
totalSeats={100}
svgProps={{
className: "my-chart",
"aria-label": "Parliament seating chart",
style: { maxWidth: "100%" },
}}
/>Typed seat data
type Member = {
name: string;
party: string;
};
const data: HemicycleData<Member>[] = members.map((m) => ({
idx: m.seatNumber,
name: m.name,
party: m.party,
seatConfig: { color: m.partyColor },
}));
<Hemicycle<Member> data={data} rows={7} totalSeats={577} />;Seat Shapes
| Shape | Description |
| -------- | ---------------------------------------------------------------- |
| arc | Curved wedge following the concentric arc of the row (default) |
| rect | Rectangle aligned to the seat's arc midpoint |
| circle | Circle centered at the seat's midpoint |
Related
@hemicycle/core— layout engine (geometry only, no rendering)@hemicycle/vanilla— framework-free SVG renderer
Contributing
Bug reports and pull requests are welcome on GitHub.
Support
- Issues: GitHub Issues
- Source: github.com/GabrielVidal1/hemicycle
Maintainer
Gabriel Vidal — [email protected]
