components/elements/SelectWidget/SelectWidget.jsxjavascript
import "./SelectWidget.scss";
// ring diameters in em, taken directly from the Figma spec. Expressed in em
// (not rem) against the widget's own fluid font-size (see .select-widget in
// the CSS) so they scale together with the viewport without touching the
// rest of the app's rem-based sizing. Buttons sit on the edge of the
// innermost ring.
/**
* Circular story picker ("selectWidget" element type): options are spread
* evenly around a ring; selecting one shows its title in the center and arms
* the page's "next" navigation (it does not navigate by itself).
*
* @param {Object} props
* @param {Array<{id: string, text: string, icon: string}>} [props.options] - Selectable options; `id` is a view id.
* @param {"portrait"|"landscape"} [props.orientation="portrait"] - Ring/item sizing preset (from the content JSON).
* @param {string|null} props.selectedId - Currently selected option id.
* @param {function(string): void} props.onSelect - Called with the tapped option's id.
*/
export const SelectWidget = ({ options = [], orientation = "portrait", selectedId, onSelect }) => {
const selectedOption = options.find((option) => option.id === selectedId);
const BUTTON_RING_DIAMETER = orientation === "portrait" ? 76.25 : 67;
const ITEM_DIAMETER = orientation === "portrait" ? 37.5 : 28; // em
return (
<div className="select-widget">
<div className="select-widget__ring select-widget__ring--outer" />
<div className="select-widget__ring select-widget__ring--dashed" />
<div className="select-widget__ring select-widget__ring--buttons" />
{selectedOption && <div className="select-widget__title">{selectedOption.text}</div>}
{options.map((option, index) => {
// spread options evenly around the button ring, starting at the top
// (-90deg) and going clockwise
const angle = -90 + index * (360 / options.length);
const radians = (angle * Math.PI) / 180;
const x = (BUTTON_RING_DIAMETER / 2) * Math.cos(radians);
const y = (BUTTON_RING_DIAMETER / 2) * Math.sin(radians);
return (
<button
key={option.id}
type="button"
className={`select-widget__item${
selectedId === option.id ? " select-widget__item--selected" : ""
}`}
style={{
"--item-diameter": `${ITEM_DIAMETER}em`,
"--x": `${x}em`,
"--y": `${y}em`,
}}
onClick={() => onSelect(option.id)}>
{option.icon ? (
<img className="select-widget__icon" src={option.icon} alt={option.text} />
) : (
<span className="select-widget__label">{option.text}</span>
)}
</button>
);
})}
</div>
);
};