# Wheel player

A pocket media player whose click wheel is geared to its list: one turn is one pass of the rows, and the detents wrap in both directions.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/wheel-player.json
```

Registry item: `wheel-player` · [`https://robocn.dev/r/wheel-player.json`](https://robocn.dev/r/wheel-player.json)

## Notes

- The wheel is geared to the list rather than pointed at it: a detent is 360 divided by `rows`, so one full turn is exactly one pass however long the list is, and the angle and the highlighted row can never disagree.
- Scrolling off the top lands on the bottom. The detents wrap in both directions, and a drag is unwrapped onto the turn the wheel is already on, so dragging past the end carries on instead of spinning a whole revolution back.
- The hold switch is an interlock, not a colour. With `locked` on, the pointer, the keyboard and the behaviour are all taken away from the wheel at once, and the machine reports `aria-disabled`.
- The display window slides to keep the selection near the middle and stops at both ends, so the list never draws a row it does not have. Nothing here plays, times or decodes anything.
- An original archetype. No manufacturer, product line, wordmark or paint scheme is reproduced here or in the demo.

## Usage

```tsx
import { WheelPlayer } from "@/components/ui/wheel-player"

<WheelPlayer rows={8} screen="list" />

// Controlled, or a wheel you can work with a thumb.
<WheelPlayer rotation={135} />
<WheelPlayer interactive onRowChange={setRow} />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"front"` | Where the camera stands. One player, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `rotation` | `number` | — | Controlled wheel rotation in degrees. Omit it and the wheel runs behavior. |
| `behavior` | `"scroll" | "seek" | "static"` | `"scroll"` | Run the list past at a turn a cycle, or work back and forth the way a thumb hunting for a track does. |
| `rows` | `number` | `8` | Rows in the list, rounded and clamped to 3–12. One turn of the wheel covers all of them. |
| `locked` | `boolean` | `false` | The hold switch. On, nothing moves the wheel — not the pointer, not the keys, not the behaviour. |
| `speed` | `number` | `0.22` | Turns of the wheel per second. |
| `animate` | `boolean` | `true` | Off parks the machine at phase and stops rendering. A reduced-motion preference does the same. |
| `paused` | `boolean` | `false` | Freeze where it stands. |
| `phase` | `number` | `0` | Seconds of offset, so a row of machines breaks step. |
| `interactive` | `boolean` | `false` | Drag round the wheel, or arrow-key it a row at a time. |
| `onRotationChange` | `(rotation: number) => void` | — | Wheel angle in degrees throughout a drag or a key press. |
| `onRowChange` | `(row: number) => void` | — | The row the wheel has landed on, 0-based. |
| `screen` | `"list" | "now-playing" | "off"` | `"list"` | What the display is showing. Structure in palette roles, never an application's own artwork. |
| `label` | `string` | — | Caption below the state readout. |
| `variant` | `"solid" | "outline" | "blueprint" | "wire"` | `"solid"` | How the machine is painted. Geometry never changes between variants. |
| `size` | `"xs" | "sm" | "md" | "lg" | "xl" | number` | `"md"` | Rendered width in pixels, or a step on the scale. |
| `color` | `string` | `var(--robot-shell)` | Body panels — the colour the machine reads as. |
| `accent` | `string` | `var(--robot-accent)` | Status colour: tip light, live tool, readouts. |
| `metal` | `string` | `var(--robot-metal)` | Bare machined parts: collars, bolts, tool bodies. |
| `dark` | `string` | `var(--robot-dark)` | Cast joints, base, shadow side. |
| `palette` | `Partial<RobotPalette>` | — | Override any subset of roles at once, including glow and grid. |

## Source

- `src/components/ui/wheel-player.tsx`
