Skip to content

Commit

Permalink
Adding a11y stuff and screwing around with css
Browse files Browse the repository at this point in the history
  • Loading branch information
rhiannanberry committed Apr 23, 2021
1 parent 33717c9 commit c4a7113
Show file tree
Hide file tree
Showing 12 changed files with 474 additions and 172 deletions.
7 changes: 5 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<html>
<head>
<meta charset="utf-8">
<title>IEEE VR 2020 | Avatar Customizer</title>
<meta name="description" content="IEEE VR 2020 | Avatar Customizer">
<title>Avatar Customizer</title>
<meta name="description" content="Avatar Customizer">
</head>
<body>
<div id="container">
Expand All @@ -13,5 +13,8 @@
<div id='options'></div>

</div>
<div>
Hello
</div>
</body>
</html>
45 changes: 38 additions & 7 deletions src/components/avatar_part_radio_group.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { Component, createRef, RefObject } from 'react';
import * as PropTypes from 'prop-types';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Expand All @@ -11,26 +11,39 @@ interface AvatarPartRadioGroupProps {
avatarPart: AvatarPart;
iconPaths: string[];
labels: string[];
title: string;
}

export default class AvatarPartRadioGroup extends Component {
disabled: boolean;
isRequired: boolean;
props: AvatarPartRadioGroupProps;
idPrefix: string;
partsRefs: RefObject<Radio>[] = [];

static propTypes = {
avatarPart: PropTypes.instanceOf(AvatarPart),
iconPaths: PropTypes.arrayOf(PropTypes.string),
labels: PropTypes.arrayOf(PropTypes.string),
title: PropTypes.string,
};

constructor(props: AvatarPartRadioGroupProps) {
super(props);

this.idPrefix = this.props.title.replace(/\s/g, "-").toLowerCase();

this.isRequired = this.props.avatarPart.isRequired;

this.disablePart = this.disablePart.bind(this);
this.togglePart = this.togglePart.bind(this);

this.moveFocus = this.moveFocus.bind(this);

const refCount = this.props.labels.length + (this.isRequired ? 0 : 1);
for (let i = 0; i < refCount; i++) {
this.partsRefs.push(createRef<Radio>());
}
}

componentDidMount(): void {
Expand All @@ -45,6 +58,12 @@ export default class AvatarPartRadioGroup extends Component {
}
}

moveFocus(index: number, direction: number): void {
const length = this.partsRefs.length;
const ind = (index + direction + length) % length;
this.partsRefs[ind].current.focus();
}

disablePart(): void {
this.props.avatarPart.disable();
this.forceUpdate();
Expand All @@ -56,29 +75,41 @@ export default class AvatarPartRadioGroup extends Component {
}

render(): JSX.Element {
const d = this.isRequired ? 0 : 1;
const disableButton = this.isRequired ? null : (
<Radio onClickCallback={this.disablePart} selected={this.props.avatarPart.disabled} className="part">
<Radio onClickCallback={this.disablePart}
ref={this.partsRefs[0]}
onMoveFocus={(dir: number) => this.moveFocus(0, dir)}
selected={this.props.avatarPart.disabled}
className="part"
label='Disable'>
<FontAwesomeIcon className="icon" icon={faBan} />
</Radio>
);

const parts = this.props.iconPaths.map((path, i) => (
<Radio
key={i}
ref={this.partsRefs[d+i]}
onMoveFocus={(dir: number) => this.moveFocus(d+i, dir)}
className="part"
onClickCallback={this.togglePart}
value={i}
label={this.props.labels[i]}
selected={!this.props.avatarPart.disabled && this.props.avatarPart.isSelected(i)}
icon={path}
>
<img className="icon" src={path} />
</Radio>
));

return (
<div className="swatchContainer">
{disableButton}
{parts}
</div>
<>
<h3 id={`${this.idPrefix}-label`}>{this.props.title}</h3>
<div id={this.idPrefix} className="swatchContainer part-container" role='radiogroup' aria-labelledby={`${this.idPrefix}-label`}>
{disableButton}
{parts}
</div>
</>
);
}
}
48 changes: 29 additions & 19 deletions src/components/color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ class MyColorPicker extends Component {

render(): JSX.Element {
const style = {
width: '100%',
position: 'relative',
display: 'block',
height: '60px',
maxWidth: '318px',
} as React.CSSProperties;

Expand All @@ -70,24 +68,36 @@ class MyColorPicker extends Component {
position: 'relative',
} as React.CSSProperties;

const sp = ()=> (
<div className='saturation-pointer'></div>
)

const hp = ()=> (
<div className='hue-pointer'></div>
)

return (
<span style={style}>
<div style={saturationStyle}>
<Saturation
// @ts-ignore
hsl={this.state.hsl}
hsv={this.state.hsv}
onChange={this.onChange}
/>
</div>
<div style={hueStyle}>
<Hue
// @ts-ignore
hsl={this.state.hsl}
onChange={this.onChange}
/>
</div>
</span>
<div className="color-picker-wrapper">
<span style={style} className='color-picker'>
<div style={saturationStyle}>
<Saturation
// @ts-ignore
hsl={this.state.hsl}
hsv={this.state.hsv}
pointer={sp}
onChange={this.onChange}
/>
</div>
<div style={hueStyle}>
<Hue
// @ts-ignore
hsl={this.state.hsl}
onChange={this.onChange}
pointer={hp}
/>
</div>
</span>
</div>
);
}
}
Expand Down
64 changes: 50 additions & 14 deletions src/components/color_radio_group.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import React, { Component } from 'react';
import React, { Component, createRef, RefObject } from 'react';
import * as PropTypes from 'prop-types';
import tinycolor from 'tinycolor2';
import { ColorResult } from 'react-color';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBan } from '@fortawesome/free-solid-svg-icons/faBan';
import { faPalette } from '@fortawesome/free-solid-svg-icons/faPalette';

import { Material } from '../models/materials/material';

import Radio from './radio';
import MyColorPicker from './color';
import { faTint } from '@fortawesome/free-solid-svg-icons';

interface ColorRadioGroupProps {
title: string;
materials: Material[];
colors: string[];
}
Expand All @@ -22,8 +21,11 @@ export default class ColorRadioGroup extends Component {
disabled: boolean;
selectedColor: string;
customColor: string;
colorRefs: RefObject<Radio>[] = [];
idPrefix: string;

static propTypes = {
title: PropTypes.string,
materials: PropTypes.arrayOf(PropTypes.instanceOf(Material)),
colors: PropTypes.arrayOf(PropTypes.string),
};
Expand All @@ -32,11 +34,19 @@ export default class ColorRadioGroup extends Component {
super(props);

this.customColor = tinycolor.random().toHexString();
this.idPrefix = this.props.title.replace(/\s/g, "-").toLowerCase();

const refCount = this.props.colors.length + 1 + (!this.props.materials[0].isRequired ? 1 : 0);

for (let i = 0; i< refCount; i++) {
this.colorRefs.push(createRef<Radio>());
}

this.disableMaterial = this.disableMaterial.bind(this);
this.setColor = this.setColor.bind(this);
this.setCustomColor = this.setCustomColor.bind(this);
this.setToCustomColor = this.setToCustomColor.bind(this);
this.moveFocus = this.moveFocus.bind(this);
}

componentDidMount(): void {
Expand All @@ -46,6 +56,12 @@ export default class ColorRadioGroup extends Component {
this.setColor(this.props.colors[index]);
}

moveFocus(index: number, direction: number): void {
const length = this.colorRefs.length;
const ind = (index + direction + length) % length;
this.colorRefs[ind].current.focus();
}

setColor(color: string): void {
this.disabled = false;
this.selectedColor = color;
Expand Down Expand Up @@ -79,8 +95,12 @@ export default class ColorRadioGroup extends Component {
let isSelected = this.disabled;

const disableButton = isRequired ? null : (
<Radio onClickCallback={this.disableMaterial} selected={this.disabled}>
<FontAwesomeIcon className="icon" icon={faBan} />
<Radio onClickCallback={this.disableMaterial}
selected={this.disabled}
ref={this.colorRefs[0]}
onMoveFocus={(dir: number) => this.moveFocus(0, dir)}
label='Disable'
faIcon={faBan}>
</Radio>
);

Expand All @@ -91,27 +111,43 @@ export default class ColorRadioGroup extends Component {
const colors = this.props.colors.map((color, i) => (
<Radio
key={i}
ref={this.colorRefs[i+(isRequired?0:1)]}
color={color}
onClickCallback={this.setColor}
selected={!this.disabled && this.selectedColor == color}
onMoveFocus={(dir: number) => this.moveFocus(i+(isRequired?0:1), dir)}
label={color}
setTitle
/>
));

//TODO: make setting title on custom color work
const customColorButton = (
<Radio onClickCallback={this.setToCustomColor} selected={!isSelected} color={this.customColor} setTitle>
<FontAwesomeIcon className="icon" icon={faPalette} />
<Radio
ref={this.colorRefs[this.colorRefs.length-1]}
onClickCallback={this.setToCustomColor}
selected={!isSelected}
color={this.customColor}
className='custom-color'
onMoveFocus={(dir: number) => this.moveFocus(this.colorRefs.length-1, dir)}
label='Custom Color'
faIcon={faTint}
setTitle>
</Radio>
);

return (
<div className="swatchContainer">
{disableButton}
{colors}
{customColorButton}
<MyColorPicker color={this.customColor} onChange={this.setCustomColor} />
</div>
<>
<h3 id={`${this.idPrefix}-label`}>{this.props.title}</h3>
<div className="swatchContainer color-container ">
<div id={this.idPrefix} className="color-group" role="radiogroup" aria-labelledby={`${this.idPrefix}-label`}>
{disableButton}
{colors}
{customColorButton}
</div>
<MyColorPicker color={this.customColor} onChange={this.setCustomColor} />
</div>
</>
);
}
}
Loading

0 comments on commit c4a7113

Please sign in to comment.