react-wheel-menu
Version:
React components & hooks for building a radial wheel menu
225 lines (190 loc) • 8.48 kB
Markdown
# react-wheel-menu
<p>
<a href="https://www.npmjs.com/package/react-wheel-menu" target="_blank" rel="noreferrer"><img alt="npm" src="https://img.shields.io/npm/v/react-wheel-menu.svg" /></a>
<a href="https://github.com/michaelbull/react-wheel-menu/actions?query=workflow%3Aci" target="_blank" rel="noreferrer"><img alt="CI Status" src="https://github.com/michaelbull/react-wheel-menu/workflows/ci/badge.svg" /></a>
<a href="https://github.com/michaelbull/react-wheel-menu/blob/master/LICENSE" target="_blank" rel="noreferrer"><img alt="License" src="https://img.shields.io/github/license/michaelbull/react-wheel-menu.svg" /></a>
</p>
React components & hooks for building a [radial wheel menu][wikipedia-pie-menu].
## Installation
```shell
npm install --save react-wheel-menu
```
## Showcase
<table>
<tbody>
<tr>
<td align="center" width="33%">
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--counter-strike" target="_blank"><img src="images/counterstrike.png" alt="Counter-Strike" /></a>
<br />
<strong>Counter-Strike</strong>
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--counter-strike" target="_blank">Demo</a>
·
<a href="https://github.com/michaelbull/react-wheel-menu/blob/master/src/stories/Examples/CounterStrike/CounterStrike.stories.tsx" target="_blank">Code</a>
<br />
</td>
<td align="center" width="33%">
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--overwatch" target="_blank"><img src="images/overwatch.png" alt="Overwatch" /></a>
<br />
<strong>Overwatch</strong>
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--overwatch" target="_blank">Demo</a>
·
<a href="https://github.com/michaelbull/react-wheel-menu/blob/master/src/stories/Examples/Overwatch/Overwatch.stories.tsx" target="_blank">Code</a>
<br />
</td>
<td align="center" width="33%">
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--grand-theft-auto" target="_blank"><img src="images/gta.png" alt="Grand Theft Auto" /></a>
<br />
<strong>Grand Theft Auto</strong>
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--grand-theft-auto" target="_blank">Demo</a>
·
<a href="https://github.com/michaelbull/react-wheel-menu/blob/master/src/stories/Examples/GrandTheftAuto/GrandTheftAuto.stories.tsx" target="_blank">Code</a>
<br />
</td>
</tr>
<tr>
<td align="center" width="33%">
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--league-of-legends" target="_blank"><img src="images/league.png" alt="League of Legends" /></a>
<br />
<strong>League of Legends</strong>
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--league-of-legends" target="_blank">Demo</a>
·
<a href="https://github.com/michaelbull/react-wheel-menu/blob/master/src/stories/Examples/LeagueOfLegends/LeagueOfLegends.stories.tsx" target="_blank">Code</a>
<br />
</td>
<td align="center" width="33%">
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--genshin-impact" target="_blank"><img src="images/genshin.png" alt="Genshin Impact" /></a>
<br />
<strong>Genshin Impact</strong>
<br />
<a href="https://michaelbull.github.io/react-wheel-menu/?path=/story/examples--genshin-impact" target="_blank">Demo</a>
·
<a href="https://github.com/michaelbull/react-wheel-menu/blob/master/src/stories/Examples/GenshinImpact/GenshinImpact.stories.tsx" target="_blank">Code</a>
<br />
</td>
</tr>
</tbody>
</table>
## Example
```tsx
import type { CSSProperties } from 'react';
import {
Label,
Layout,
Segment,
Wheel
} from 'react-wheel-menu';
import 'react-wheel-menu/index.css';
export function Example() {
const red: CSSProperties = { backgroundColor: 'red', color: 'white' };
const yellow: CSSProperties = { backgroundColor: 'yellow', color: 'black' };
const green: CSSProperties = { backgroundColor: 'green', color: 'white' };
const blue: CSSProperties = { backgroundColor: 'blue', color: 'white' };
function onClickBlue(event: MouseEvent<HTMLButtonElement>) {
console.log('Clicked blue', event);
}
return (
<Wheel style={{ width: 300, height: 300 }}>
<Segment from={315} to={45} style={red}>
<Layout>
<Label>Red</Label>
</Layout>
</Segment>
<Segment from="45deg" to="37.5%" style={yellow}>
<Layout direction="vertical" justify="start">
<Label orient="outwards" offset={-30}>Start</Label>
</Layout>
<Layout direction="horizontal" justify="center">
<Label orient="upwards" offset={-10}>Up</Label>
<Label orient="downwards" offset={-10}>Down</Label>
</Layout>
<Layout direction="vertical" justify="end">
<Label orient="inwards" offset={10}>End</Label>
</Layout>
</Segment>
<Segment from="37.5%" to="3.92699rad" style={blue} as="button" onClick={onClickBlue}>
<Layout>
<Label>Blue</Label>
</Layout>
</Segment>
<Segment from="3.92699rad" to="0.875turn" style={green} as="a" href="https://example.com" target="_blank">
<Layout>
<Label>Green</Label>
</Layout>
</Segment>
</Wheel>
);
}
```
## Components
### `<Segment>`
A `<Segment>` must specify the angle it starts `from` and spans `to`, for example:
- A right-angled segment in [degrees][degree]:
- `<Segment from={0} to={90} />`
- An acute-angled segment in [radians][radian]:
- `<Segment from="1rad" to="2rad" />`
- An obtuse-angled segment in [gradians][gradian]:
- `<Segment from="100grad" to="210grad" />`
- A right-angled segment in [turns][turn]:
- `<Segment from="0.5turn" to="0.75turn" />`
- An obtuse-angled segment in percentages:
- `<Segment from="50%" to="85%" />`
### `<Spoke>`
A `<Spoke>` is a straight line radiating from the centre of the `<Wheel>`.
Spokes can be used to visually split segments, for example:
```tsx
<Wheel style={{ width: 300, height: 300 }}>
<Segment from="0deg" to="45deg" style={{ background: 'red' }} />
<Segment from="45deg" to="90deg" style={{ background: 'blue' }} />
<Spoke angle="45deg" style={{ background: 'white', height: '100px' }} />
</Wheel>
```
### `<Layout>`
A `<Layout>` controls the distribution and orientation of `<Label>`s inside a
`<Segment>` or `<Spoke>`.
In the `<Layout>` below, the `<Label>` is positioned at the edge of the segment
with the text facing inwards to the center of the wheel.
```tsx
<Wheel style={{ width: 300, height: 300 }}>
<Segment from={0} to={90}>
<Layout direction="vertical" justify="end">
<Label orient="inwards">Inwards Label</Label>
</Layout>
</Segment>
<Spoke angle="45deg" style={{ width: '10px' }}>
<Layout direction="vertical">
<Label orient="inwards">Outwards Label</Label>
</Layout>
</Spoke>
</Wheel>
```
## Contributing
Bug reports and pull requests are welcome on [GitHub][github].
## License
This project is available under the terms of the ISC license. See the
[`LICENSE`](LICENSE) file for the copyright information and licensing terms.
[npm]: https://www.npmjs.com/package/react-wheel-menu
[github]: https://github.com/michaelbull/react-wheel-menu
[wikipedia-pie-menu]: https://en.wikipedia.org/wiki/Pie_menu
[degree]: https://en.wikipedia.org/wiki/Degree_(angle)
[radian]: https://en.wikipedia.org/wiki/Radian
[gradian]: https://en.wikipedia.org/wiki/Gradian
[turn]: https://en.wikipedia.org/wiki/Turn_(angle)