summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/lobby/radial-list/RadialList.css23
-rw-r--r--frontend/src/components/lobby/radial-list/RadialList.jsx65
-rw-r--r--frontend/src/components/lobby/radial-list/RadialListItem.css11
-rw-r--r--frontend/src/components/lobby/radial-list/RadialListItem.jsx18
-rw-r--r--frontend/src/components/lobby/radial-list/radial-math.js50
5 files changed, 167 insertions, 0 deletions
diff --git a/frontend/src/components/lobby/radial-list/RadialList.css b/frontend/src/components/lobby/radial-list/RadialList.css
new file mode 100644
index 00000000..3b0f3a79
--- /dev/null
+++ b/frontend/src/components/lobby/radial-list/RadialList.css
@@ -0,0 +1,23 @@
+.radial-list-container {
+ margin: 0;
+ padding: 0;
+ position: relative;
+}
+
+.radial-list {
+ margin: 0;
+ padding: 0;
+ transition: all 500ms ease-in-out;
+ z-index: 1;
+}
+
+.radial-list-center {
+ z-index: 0;
+}
+
+.absolute-center {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
diff --git a/frontend/src/components/lobby/radial-list/RadialList.jsx b/frontend/src/components/lobby/radial-list/RadialList.jsx
new file mode 100644
index 00000000..95ed8d17
--- /dev/null
+++ b/frontend/src/components/lobby/radial-list/RadialList.jsx
@@ -0,0 +1,65 @@
+//@flow
+import * as React from 'react';
+import type { CartesianCoords, RadialConfig } from './radial-math';
+import { cartesianOffsets, CLOCKWISE, COUNTERCLOCKWISE } from './radial-math';
+import './RadialList.css';
+import { RadialListItem } from './RadialListItem';
+
+type RadialListProps = {
+ items: Array<React.Node>,
+ centerElement?: React.Node,
+ diameter?: number, // 240px by default
+ offsetDegrees?: number, // defaults to 0 = 12 o'clock
+ arc?: number, // defaults to 360 (full circle)
+ clockwise?: boolean, // defaults to 360 (full circle)
+ itemWidth?: number,
+ itemHeight?: number,
+};
+
+export const RadialList = ({items, centerElement, diameter = 240, offsetDegrees = 0, arc = 360, clockwise = true, itemWidth = 20, itemHeight = 20}: RadialListProps) => {
+ const containerStyle = {
+ width: diameter + itemWidth,
+ height: diameter + itemHeight,
+ };
+ const radius = diameter / 2;
+ const direction = clockwise ? CLOCKWISE : COUNTERCLOCKWISE;
+ const radialConfig: RadialConfig = {radius, arc, offsetDegrees, direction};
+
+ return <div className='radial-list-container' style={containerStyle}>
+ <RadialListItems items={items} radialConfig={radialConfig}/>
+ <RadialListCenter centerElement={centerElement}/>
+ </div>;
+};
+
+type RadialListItemsProps = {
+ items: Array<React.Node>,
+ radialConfig: RadialConfig,
+};
+
+const RadialListItems = ({items, radialConfig}: RadialListItemsProps) => {
+ const diameter = radialConfig.radius * 2;
+ const ulStyle = {
+ width: diameter,
+ height: diameter,
+ };
+ const itemOffsets: Array<CartesianCoords> = cartesianOffsets(items.length, radialConfig);
+
+ return <ul className='radial-list absolute-center' style={ulStyle}>
+ {items.map((item, i) => (<RadialListItem
+ key={i}
+ item={item}
+ offsets={itemOffsets[i]}
+ />))}
+ </ul>;
+};
+
+type RadialListCenterProps = {
+ centerElement?: React.Node,
+};
+
+const RadialListCenter = ({centerElement}: RadialListCenterProps) => {
+ if (!centerElement) {
+ return null;
+ }
+ return <div className='radial-list-center absolute-center'>{centerElement}</div>;
+};
diff --git a/frontend/src/components/lobby/radial-list/RadialListItem.css b/frontend/src/components/lobby/radial-list/RadialListItem.css
new file mode 100644
index 00000000..65bb9661
--- /dev/null
+++ b/frontend/src/components/lobby/radial-list/RadialListItem.css
@@ -0,0 +1,11 @@
+.radial-list-item {
+ display: block;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: 0;
+ padding: 0;
+ list-style: unset;
+ transition: all 500ms ease-in-out;
+ z-index: 1;
+}
diff --git a/frontend/src/components/lobby/radial-list/RadialListItem.jsx b/frontend/src/components/lobby/radial-list/RadialListItem.jsx
new file mode 100644
index 00000000..1d5df7f5
--- /dev/null
+++ b/frontend/src/components/lobby/radial-list/RadialListItem.jsx
@@ -0,0 +1,18 @@
+//@flow
+import * as React from 'react';
+import type { CartesianCoords } from './radial-math';
+import './RadialListItem.css';
+
+type RadialListItemProps = {
+ item: React.Node,
+ offsets: CartesianCoords,
+};
+
+export const RadialListItem = ({item, offsets}: RadialListItemProps) => {
+ // Y-axis points down, hence the minus sign
+ const liStyle = {
+ transform: `translate(${offsets.x}px, ${-offsets.y}px) translate(-50%, -50%)`,
+ };
+
+ return <li className='radial-list-item' style={liStyle}>{item}</li>;
+};
diff --git a/frontend/src/components/lobby/radial-list/radial-math.js b/frontend/src/components/lobby/radial-list/radial-math.js
new file mode 100644
index 00000000..4091a270
--- /dev/null
+++ b/frontend/src/components/lobby/radial-list/radial-math.js
@@ -0,0 +1,50 @@
+//@flow
+
+export type CartesianCoords = {
+ x: number,
+ y: number,
+}
+type CylindricalCoords = {
+ radius: number,
+ thetaDegrees: number,
+}
+
+const toRad = (deg) => deg * (Math.PI / 180);
+const roundedProjection = (radius, theta, trigFn) => Math.round(radius * trigFn(theta));
+const xProjection = (radius, theta) => roundedProjection(radius, theta, Math.cos);
+const yProjection = (radius, theta) => roundedProjection(radius, theta, Math.sin);
+
+const toCartesian = ({radius, thetaDegrees}: CylindricalCoords): CartesianCoords => ({
+ x: xProjection(radius, toRad(thetaDegrees)),
+ y: yProjection(radius, toRad(thetaDegrees)),
+});
+
+export type Direction = -1 | 1;
+export const CLOCKWISE: Direction = -1;
+export const COUNTERCLOCKWISE: Direction = 1;
+
+export type RadialConfig = {|
+ radius: number,
+ arc: number,
+ offsetDegrees: number,
+ direction: Direction,
+|}
+const DEFAULT_CONFIG: RadialConfig = {
+ radius: 120,
+ arc: 360,
+ offsetDegrees: 0,
+ direction: CLOCKWISE,
+};
+
+const DEFAULT_START = 90; // Up
+
+export function cartesianOffsets(nbItems: number, radialConfig: RadialConfig = DEFAULT_CONFIG): Array<CartesianCoords> {
+ return Array.from({length: nbItems}, (v, i) => itemCartesianOffsets(i, nbItems, radialConfig));
+}
+
+function itemCartesianOffsets(index: number, nbItems: number, {radius, arc, direction, offsetDegrees}: RadialConfig): CartesianCoords {
+ const startAngle = DEFAULT_START + direction * offsetDegrees;
+ const angleStep = arc / nbItems;
+ const itemAngle = startAngle + direction * angleStep * index;
+ return toCartesian({radius, thetaDegrees: itemAngle});
+}
bgstack15