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>
  );
};