From e6e2acd5dd59b87dcaa3ccc8fdc108e13e9595ad Mon Sep 17 00:00:00 2001 From: Joffrey Bion Date: Mon, 11 Jun 2018 19:56:56 +0200 Subject: Add first draft of radial list --- .../components/lobby/radial-list/RadialList.css | 23 ++++++++ .../components/lobby/radial-list/RadialList.jsx | 65 ++++++++++++++++++++++ .../lobby/radial-list/RadialListItem.css | 11 ++++ .../lobby/radial-list/RadialListItem.jsx | 18 ++++++ .../components/lobby/radial-list/radial-math.js | 50 +++++++++++++++++ 5 files changed, 167 insertions(+) create mode 100644 frontend/src/components/lobby/radial-list/RadialList.css create mode 100644 frontend/src/components/lobby/radial-list/RadialList.jsx create mode 100644 frontend/src/components/lobby/radial-list/RadialListItem.css create mode 100644 frontend/src/components/lobby/radial-list/RadialListItem.jsx create mode 100644 frontend/src/components/lobby/radial-list/radial-math.js (limited to 'frontend/src/components/lobby') 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, + 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
+ + +
; +}; + +type RadialListItemsProps = { + items: Array, + radialConfig: RadialConfig, +}; + +const RadialListItems = ({items, radialConfig}: RadialListItemsProps) => { + const diameter = radialConfig.radius * 2; + const ulStyle = { + width: diameter, + height: diameter, + }; + const itemOffsets: Array = cartesianOffsets(items.length, radialConfig); + + return
    + {items.map((item, i) => ())} +
; +}; + +type RadialListCenterProps = { + centerElement?: React.Node, +}; + +const RadialListCenter = ({centerElement}: RadialListCenterProps) => { + if (!centerElement) { + return null; + } + return
{centerElement}
; +}; 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
  • {item}
  • ; +}; 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 { + 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}); +} -- cgit