<main id='rootUI'></main>
body{
	background: none;
	color: dimgray;
	height: 100vh;
	font-family: Verdana, Geneva, sans-serif;
}
a{
	text-decoration: none;
}
.navbarContainer{
	top:0;
	right:0;
	left:0;
	position:fixed;
	min-height:40px;
	margin-bottom:20px;
	z-index:1000;
	background:silver;
	border-radius:3px;
	box-shadow: 0 0 5px 2px dimgray;
	display: flex;
	font-family: sans-serif;
	font-size: 1em;
	line-height: 1.3em;
	padding:0;
	justify-content:space-between;
}
.navbarContainer a{
	color: dimgray;
}
.navLeft, .navRight{
	display: flex;
}
.navLeft a, .navRight a{
	padding: 8px 12px 0px 12px;
}
.navLeft a:hover, .navRight a:hover{
	background: dimgray;
	color:white;
	border-bottom: solid 1px gray;
	border-radius: 3px;
}
.dropMenu{
}
main{
	position: absolute;
	display: flex;
	flex-direction: column;
	top: 60px;
	left: 50%;
}
.inputWarn{
	font-size: 0.6em;
	color: crimson
}
.nameTitle{
	text-align: center;
}
.mainContainer{
	width: 280px;
	margin: 0 0 0 -140px;
	border-top: ridge 1px white;
	box-shadow: 0px 2px 4px 2px dimgray;
	padding: 0 0 10px 0;
}
.dSelect{
	min-width: 128px;
	color: dimgray;
}
.dSelect:after{
	background: blue;
}
.abilityInputs{
	display: grid;
	grid-template-columns: 40% 10% 10% 10% 10% 10% 10%;
	font-size: 0.9em;
}
.abilityResult{
	margin: 4px 8px 4px 8px;
	display: grid;
	grid-template-columns: 60% 20% 20%;
	font-size: 0.9em;
}
.savingResult{
	display: grid;
	grid-template-columns: 80% 20%;
	font-size: 0.9em;
}
.skillSelect{
	display: grid;
	grid-template-columns: 10% 70% 20%;
	font-size: 0.9em;
}
.numGrid{
	text-align: right;
	padding-right: 30%;
}
.strHead{
	font-weight: 600;
}
.numHead{
	font-weight: 600;
	text-align: center;
}




@media (max-width: 610px){
	.navRight .navDrop{
		display: block;
	}
	.dropMenu{
		position: absolute;
		right: 4px;
		top: 40px;
		z-index: 1000;
		display: flex;
		flex-direction: column;
		background: silver;
		margin-top: 5px;
		box-shadow: 0 0 4px 2px gray;
	}
	.dropMenu a{
		color: dimgray;
		min-height: 30px;
		font-family: sans-serif;
		font-size: 1em;
		line-height: 1em;
		padding: 9px 12px 0px 12px;
		text-align: center;
	}
	.dropMenu a:hover{
		background: dimgray;
		color: white;
	}
}
// IMPORT FUNCTIONS FOR REACT AND REDUX
const {
	createStore,
	bindActionCreators
} = Redux;
const {
	Provider,
	connect
} = ReactRedux;
const render = ReactDOM.render

// DESIGNATE DEFAULT STATE FOR REDUX
const DataList={
		'Name':null,
		'Class':null, 
		'Race':null, 
		'Background':null, 
		'Modifiers':[0,0,0,0,0,0],
		'Abilities':[0,0,0,0,0,0], 
		'Saving Throws':[0,0,0,0,0,0], 
		'Skills':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
		'Speed':0,
		'Hit Points':0,
		'Proficiency':2
	};
// FUNCTIONS

/*
CALLS A PROVIDED FUNCTION ON EACH PROPERTY OF THE PROVIDED OBJECT; RETURNS AN ARRAY
ARGUMENTS: {{}, function(key, value, index)}
*/
function mapObject(obj, callback) {
  return Object.keys(obj).map((k, i)=>{
    return callback(k, obj[k], i);
  });
}
/*
#####################################################
# 																	 #
#  NAVBAR NAVBAR NAVBAR NAVBAR NAVBAR NAVBAR NAVBAR #
# 																	 #
#####################################################
*/
// GLOBALS
const LOGO = {'Character Builder':'#'};
const SITELINKS = {};
const KEYTRACK = [1];
const TABINDEX = [];
const TWEETPRE = 'https://twitter.com/intent/tweet?text=';
// REACT
/* LINKS FOR NAVBAR LOCATIONS
PROPS: {places: {}}
*/
const NavPlaces = (props) =>{
	KEYTRACK[0]++;
	return mapObject(props.places, function (key, value, iter) {
		return (<a 
					  href={value} 
					  key={key+KEYTRACK[0].toString()+iter.toString()}>{key}
			</a>);
	});
}
NavPlaces.propTypes={
	places: PropTypes.objectOf(PropTypes.string).isRequired
}
/* LEFT NAVBAR
PROPS: {sWidth: #, places: {}}
*/
const LeftNav = (props) =>{
	return(<div id={'dispLeft'} className={'navLeft'}>
			<NavPlaces places={LOGO} />
			{props.sWidth>610 ? <NavPlaces places={props.places} />:null}
		</div>);
}
LeftNav.propTypes={
	sWidth: PropTypes.number.isRequired,
	places: PropTypes.objectOf(PropTypes.string).isRequired
}
/* SOCIAL NETWORK LINKS
PROPS: {link: string, name: string}
*/
const SocialLink = (props)=>{
	return <a href={props.link} target='_blank'><i className={'fa fa-'+props.name}></i></a>
}
SocialLink.propTypes={
	link: PropTypes.string,
	name: PropTypes.name
}
/* RIGHT NAVBAR
PROPS: {menuHandler: function}
*/
const RightNav = (props)=>{
	return(<div id={'dispRight'} className={'navRight'}>
			<SocialLink 
				link={props.tweetLink} 
				name={'twitter'}/>
			{props.sWidth<611 ?
				(<a href='#' id={'responseMenu'} class={'navDrop'} onClick={props.menuHandler}>
					<i class='fa fa-bars'></i>
				</a>):
			null}
		</div>);
}
RightNav.propTypes={
	menuHandler: PropTypes.func.isRequired
}
/* NAVBAR CONTAINER FOR LEFT AND RIGHT NAVBARS
PROPS: {logo: {}, places: {}, sWidth: #, menuHandler:function}
*/
const NavParent = (props)=>{
	return(<nav className={'navbarContainer'}>
			<LeftNav logo={props.logo} places={props.places} sWidth={props.sWidth}/>
			<RightNav sWidth={props.sWidth} menuHandler={props.menuHandler} tweetLink={props.tweetLink} />
		</nav>
			);
}
NavParent.propTypes={
	logo: PropTypes.objectOf(PropTypes.string).isRequired,
	places: PropTypes.objectOf(PropTypes.string).isRequired,
	sWidth: PropTypes.number.isRequired,
	menuHandler: PropTypes.func.isRequired
}
/* NAVBAR DROPDOWN COMPONENT
PROPS: {places:{}, menuHandler:function}
*/
const ResponsiveNav = (props)=>{
	return(<div className={'dropMenu'} onClick={props.menuHandler}>
				<NavPlaces places={props.places}/>
			</div>
	);
}
ResponsiveNav.propTypes={
	places: PropTypes.objectOf(PropTypes.string).isRequired,
	menuHandler: PropTypes.func.isRequired
}
/* NAVBAR PARENT COMPONENT
CURRENTLY NO PROPS
*/
class Navbar extends React.Component {
	constructor(props){
		super(props);
		this.state={
			width: window.innerWidth,
			respondMenu: false
		};
		this.updateWidth=this.updateWidth.bind(this);
		this.responsiveClickHandler=this.responsiveClickHandler.bind(this);
	}
	componentDidMount(){
		window.addEventListener('resize', this.updateWidth);
	}
	updateWidth(){
		this.setState({width: window.innerWidth});
	}
	responsiveClickHandler(){
		this.setState(p=>({respondMenu:!p.respondMenu}));
	}
	render(){
		return(<div>
			<NavParent 
				logo={LOGO} 
				places={SITELINKS}
				tweetLink={this.props.tweetLink}
				sWidth={this.state.width} 
				menuHandler={this.responsiveClickHandler} />
				{this.state.respondMenu?
			<ResponsiveNav 
				 places={SITELINKS}
				 menuHandler={this.responsiveClickHandler}/>:
				null}
		</div>);
	}
	componentWillUnmount(){
		window.removeEventListener('resize', this.updateDimensions);
	}
}

/*
#####################################################
# 																	 #
#   MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN    #
# 																	 #
#####################################################
*/
// GLOBALS
const SUBJECTLIB = {
	'Class':['Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter', 'Monk', 'Paladin', 'Ranger', 'Rogue', 'Sorcerer', 'Warlock', 'Wizard'],
	'Race':['Hill Dwarf', 'Mountain Dwarf', 'High Elf', 'Wood Elf', 'Dark Elf', 'Lightfoot Halfling', 'Stout Halfling', 'Human', 'Dragonborn', 'Forest Gnome', 'Rock Gnome', 'Half-Elf', 'Half-Orc', 'Tiefling'],
	'Background':['Acolyte', 'Charlatan', 'Criminal', 'Entertainer', 'Folk Hero', 'Guild Artisan', 'Hermit', 'Noble', 'Outlander', 'Sage', 'Sailor', 'Soldier', 'Urchin'],
	'Skills':['Acrobatics', 'Animal Handling', 'Arcana', 'Athletics', 'Deception','History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival'],
	'Abilities':['Strength','Dexterity','Constitution','Intelligence','Wisdom','Charisma']
};

const ATTRIBLIB = {
	// Race : {Abilities : [STRENGTH, DEXTERITY, CONSTITUTION, INTELLIGENCE, WISDOM, CHARISMA, OPTIONAL, HIT-POINT BOOST], Walk : WALK SPEED, Size : SIZE}
	'Hill Dwarf': {'Abilities':[0,0,2,0,1,0],'Optional':0, 'Hit':1, 'Walk':25, 'Size':'Medium'},
	'Mountain Dwarf': {'Abilities':[2,0,2,0,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Medium'},
	'High Elf': {'Abilities':[0,2,0,1,0,0],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}, 
	'Wood Elf': {'Abilities':[0,2,0,0,1,0],'Optional':0, 'Hit':0, 'Walk':35, 'Size':'Medium'}, 
	'Dark Elf': {'Abilities':[0,2,0,0,0,1],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}, 
	'Lightfoot Halfling': {'Abilities':[0,2,0,0,0,1],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'}, 
	'Stout Halfling': {'Abilities':[0,2,1,0,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'}, 
	'Human': {'Abilities':[1,1,1,1,1,1],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}, 
	'Dragonborn': {'Abilities':[2,0,0,0,0,1],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}, 
	'Forest Gnome': {'Abilities':[0,1,0,2,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'}, 
	'Rock Gnome': {'Abilities':[0,0,1,2,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'}, 
	'Half-Elf': {'Abilities':[0,0,0,0,0,2],'Optional':2, 'Hit':0, 'Walk':30, 'Size':'Medium'}, 
	'Half-Orc': {'Abilities':[2,0,1,0,0,0],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}, 
	'Tiefling': {'Abilities':[0,0,0,1,0,2],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}
};
const MATCHSKILL = {
	'Acrobatics':'Dexterity', 'Animal Handling':'Wisdom', 'Arcana':'Intelligence', 'Athletics':'Strength', 'Deception':'Charisma','History':'Intelligence', 'Insight':'Wisdom', 'Intimidation':'Charisma', 'Investigation':'Intelligence', 'Medicine':'Wisdom', 'Nature':'Intelligence', 'Perception':'Wisdom', 'Performance':'Charisma', 'Persuasion':'Charisma', 'Religion':'Intelligence', 'Sleight of Hand':'Dexterity', 'Stealth':'Dexterity', 'Survival':'Wisdom'
}
const SKILLLIB = {
	'Barbarian':[0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1], 
	'Bard':[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], 
	'Cleric':[0,0,0,0,0,1,1,0,0,1,0,0,0,1,1,0,0,0], 
	'Druid':[0,1,1,0,0,0,1,0,0,1,1,1,0,0,1,0,0,1], 
	'Fighter':[1,1,0,1,0,1,1,1,0,0,0,1,0,0,0,0,0,1], 
	'Monk':[1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,0], 
	'Paladin':[0,0,0,1,0,0,1,1,0,1,0,0,0,1,1,0,0,0], 
	'Ranger':[0,1,0,1,0,0,1,0,1,0,1,1,0,0,0,0,1,1], 
	'Rogue':[1,0,0,1,1,0,1,1,1,0,0,1,1,1,0,1,1,0], 
	'Sorcerer':[0,0,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,0], 
	'Warlock':[0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0], 
	'Wizard':[0,0,1,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0],
	'Acolyte':[0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], 
	'Charlatan':[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0], 
	'Criminal':[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0], 
	'Entertainer':[1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], 
	'Folk Hero':[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], 
	'Guild Artisan':[0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0], 
	'Hermit':[0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0], 
	'Noble':[0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0], 
	'Outlander':[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1], 
	'Sage':[0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0], 
	'Sailor':[0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0], 
	'Soldier':[0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0], 
	'Urchin':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0]
}
const SAVINGLIB = {
	'Barbarian':[1,0,1,0,0,0], 'Bard':[0,1,0,0,0,1], 'Cleric':[0,0,0,0,1,1], 'Druid':[0,0,0,1,1,0], 'Fighter':[1,0,1,0,0,0], 'Monk':[1,1,0,0,0,0], 'Paladin':[0,0,0,0,1,1], 'Ranger':[1,1,0,0,0,0], 'Rogue':[0,1,0,1,0,0], 'Sorcerer':[0,0,1,0,0,1], 'Warlock':[0,0,0,0,1,1], 'Wizard':[0,0,0,1,1,0]
}
const HITDIE = {
	'Barbarian':12, 'Bard':8, 'Cleric':8, 'Druid':8, 'Fighter':10, 'Monk':8, 'Paladin':10, 'Ranger':10, 'Rogue':8, 'Sorcerer':6, 'Warlock':8, 'Wizard':8
}
const USESKILL = {
	'Barbarian':2, 'Bard':3, 'Cleric':2, 'Druid':2, 'Fighter':2, 'Monk':2, 'Paladin':2, 'Ranger':3, 'Rogue':4, 'Sorcerer':2, 'Warlock':2, 'Wizard':2
}
// MAIN UI FUNCTIONS
/*
GENERATES A RANDOM NUMBER BETWEEN 1 AND DESIRED VALUE
ARGUMENTS: {Number (n)}
*/
function rollADie(n){
	return Math.floor(Math.random()*n)+1;
}
/*
GENERATES FOUR RANDOM NUMBERS (1-6) AND SUMS THE THREE HIGHEST
ARGUMENTS: NONE
*/
function rollAbility(){
	var results = [];
	for (var i=0; i<4; i++){
		results.push(rollADie(6));
	}
	results.sort((a,b)=>a-b);
	results.shift()
	return results.reduce((c,d)=>c+d);
}
/* RETURNS SIX VALUES USING THE rollAbility FUNCTION*/
const rollCharacter =()=>{
	var s = [];
	for(var i=0; i<6; i++){
		s.push(rollAbility());
	}
	return s;
}
/* REMOVES NAME PROPERTY FROM UISection1 charValues
ARGS: {m {}}
*/
const selectsOnly=(m)=>{
	if (m.hasOwnProperty('Name')){ delete m.Name }
	return m;
}
/* OPTIONS FOR SELECT MENUS IN REACT COMPONENTS
ARGS: {NAME/VALUE (n), EVENT LISTENER (v)}
*/
const makeOption=(n,v)=>{
	KEYTRACK[0]++;
	return(<option className='dOption' value={n} key={n+KEYTRACK[0].toString()}>{n}</option>)
}
/* MAKE SELECT MENUS FOR REACT COMPONENT
ARGS: {LABEL (b), LIST (l), EVENT LISTENER (v), ITERABLE (i), SELECTED (s), WARN CONDITIONAL (w)}
*/
const makeSelect=(b,l,s,i,v,w)=>{
	//KEYTRACK[0]++;
	var opts= l.map((x)=> makeOption(x,v));
	return(<div className='inputContain'>
			<select 
				 className='dSelect character' 
				 name={b} 
				 onChange={v}
				 value={s?s:b}
				 key={'dontChange'+i.toString()}>
				<option
					className='dOption'
					value={b}
					disabled>{b}</option>
				{opts}
			</select>
			<div>{w[[b]]?<div className='inputWarn'>Please make a selection.</div>:null}</div>
		</div>);
}
/* REACT MENU COMPONENT
PROPS: {menus [], handler: function, warn:{}}
*/
class MakeMenus extends React.Component{
	render(){
		return(<div>
				{mapObject(this.props.menus, (x,y,i)=>makeSelect(x,SUBJECTLIB[x],y,i,this.props.handler, this.props.warn))}
			</div>);
	}
}
MakeMenus.propTypes={
	menus: PropTypes.object.isRequired,
	handler: PropTypes.func.isRequired,
	warn: PropTypes.objectOf(PropTypes.bool).isRequired
}
/* UI SECTION 1 
PROPS: {handleComplete: function, changeNCRB: function (Redux), changeSpeed: function (Redux)}
*/
class UISection1 extends React.Component{
	constructor(props){
		super(props);
		this.state={
			charValues:{'Name':null,'Class':null,'Race':null,'Background':null},
			charWarnings:{'Name':false,'Class':false,'Race':false,'Background':false},
			completed: false
		}
		this.handleCharAcc=this.handleCharAcc.bind(this);
		this.handleNameEntry=this.handleNameEntry.bind(this);
		this.handleOppClick=this.handleOppClick.bind(this);
	}
	handleNameEntry(e){
		this.setState({charValues: Object.assign({...this.state.charValues},{'Name':e.target.value})});
	}
	handleOppClick(e){
		if(e.target.name){
			this.setState({charValues: Object.assign({...this.state.charValues},{[e.target.name]:e.target.value})});
		}
		e.preventDefault();
	}
	handleCharAcc(e){
		const states=Object.assign({}, this.state.charValues);
		if(Object.values(states).reduce((x,y)=>Boolean(x)&&Boolean(y))){
			this.props.changeNCRB(states);
			this.props.changeSpeed(ATTRIBLIB[states.Race].Walk);
			e.target.disabled=true;
			this.props.handleComplete();
		} else {
			for(var i in states){states[i]=!Boolean(states[i])}
			this.setState({charWarnings:states});
		}
	}
	render(){
		//this.props.testLog();
		return(<section className='mainSection'>
				<fieldset><legend>Character</legend>
					<h4>What is your name, hero?</h4>
					<div className='inputContain'>
						<input className='textBox character' type='text' name='Name' onChange={this.handleNameEntry}></input>
						<div>{this.state.charWarnings.Name?<div className='inputWarn'>Please enter a name.</div>:null}</div>
					</div>
					<MakeMenus menus={selectsOnly({...this.state.charValues})} handler={this.handleOppClick} warn={this.state.charWarnings}/>
					<div><button onClick={this.handleCharAcc}>Accept</button></div>
				</fieldset>
			</section>);
	}
}
UISection1.propTypes={
	changeNCRB: PropTypes.func,
	changeSpeed: PropTypes.func,
	handleComplete: PropTypes.func
}
/* UI Section 1a 
PROPS: {data (Redux)}
*/
class UISection1a extends React.Component{
	constructor(props){
		super(props);
	}
	render(){
		return(<section className='mainSection'>
				<fieldset><legend>Character</legend>
					<h4 className='nameTitle'>{this.props.data.Name}</h4>
					<div className='nameTitle'>{this.props.data.Race}</div>
					<div className='nameTitle'>{this.props.data.Class}</div>
					<div className='nameTitle'>{this.props.data.Background}</div>
				</fieldset>
			</section>)
	}
}
// RETURNS INDEX; Properties: {c: LIST-NAME (''), a: VALUE ('')}
const getDescriptIndex=(c,a)=>{
	return SUBJECTLIB[c].findIndex((x)=>x===a);
}
// CUTS END OF INPUT NAME OFF TO MATCH TO OBJECT KEY: {n: NAME OF INPUT}
const nameToKey=(n)=>{
	return n.split('_')[0];
}
// MATCHES VALUES OF TWO ARRAYS; RETURNS false IF THERE IS A MISMATCH: {a,b: ARRAYS}
function matchArray(a,b){
	var c = [...b];
	for(var g=0, h=a.length; g<h; g++){
		for(var i=c.length-1; i>=0; i--){
			if(c[i]===a[g]){
				c.splice(i,1);
				break;
			}
		}		
	}
	if (c.length){ return false; } else return true;
}
/*
function checkAllValues(o){
	for (var n in o){
		if(!o[n]){
			return false;
		}
	}
	return true;
}*/

const increaseAbility=(r, a, i)=>{
	return a+ATTRIBLIB[r]['Abilities'][i];
}
const modAbility=(a)=>{
	return Math.floor((a-10)/2);
}
const savAbility=(c,m,p,i)=>{
	if (SAVINGLIB[c][i]){return p+m;}else return m;
}
const renderStats=(k,v,r,c,p)=>{
	const it=getDescriptIndex('Abilities',k);
	const ab=increaseAbility(r,v,it);
	const mo=modAbility(ab);
	const sa=savAbility(c,mo,p,it);
	return {abilities:ab,mods:mo,saves:sa};
}
// PROPS: {statChoices: [stats], ability:''}
const AbilityChoice = (props)=>{
	return props.rollValues.map((x,y)=>{
		return(<input 
					 className='numGrid'
					 key={props.ability+y.toString()} 
					 type={"radio"} 
					 name={props.ability+'_Ability'} 
					 value={x}
					 onClick={props.handleChoice}>
			</input>);
	});
}
// PROPS: {ability: '', dataR: ''(user chosen r)}
class AbilityLabel extends React.Component{
	constructor(props){
		super(props);
	}
	render(){
		const bonus = ATTRIBLIB[this.props.dataR].Abilities[getDescriptIndex('Abilities',this.props.ability)];
		return(<div htmlFor={this.props.ability+'_Ability'}>
				{this.props.ability}
				{bonus?<span>{'(+'+bonus.toString()+')'}</span>:null}
			</div>)
	}
}
// PROPS: {ability: '', dataR:'', rollValues:[], handleChoice: func, iter:#}
class AbilityInput extends React.Component{
	constructor(props){
		super(props);
	}
	render(){
		return(<div className='abilityInputs' key={'abilityInput'+this.props.iter.toString()}>
				 <AbilityLabel ability={this.props.ability} dataR={this.props.dataR}/>
				 <AbilityChoice rollValues={this.props.rollValues} ability={this.props.ability} handleChoice={this.props.handleChoice}/>
			 </div>);
		
	}
}
class DistributeTable extends React.Component{
	constructor(props){
		super(props);
	}
	render(){
		var inputs=SUBJECTLIB['Abilities'].map((x,y)=><AbilityInput 
																		 dataR={this.props.data.Race} 
																		 rollValues={this.props.rollValues} 
																		 handleChoice={this.props.handleChoice} 
																		 ability={x} 
																		 iter={y} />);
		return(<div name="abilityForm">
				{inputs}
				<button onClick={this.props.handleAccept}>Accept</button>
			</div>
		);
	}
}

class UISection2 extends React.Component{
	constructor(props){
		super(props);
		this.state={
			rollValues:[],
			optValues:{},
			abilities:{},
			saves:{},
			mods:{},
			warning:false
		}
		this.handleRoll=this.handleRoll.bind(this);
		this.handleChoice=this.handleChoice.bind(this);
		this.handleAccept=this.handleAccept.bind(this);
	}
	handleRoll(){
		this.setState({rollValues:rollCharacter()});
	}
	handleChoice(e){
		const ky=nameToKey(e.target.name);
		const st=renderStats(ky,parseInt(e.target.value),this.props.data.Race,this.props.data.Class,this.props.data.Proficiency);
		this.setState({optValues:Object.assign({},{...this.state.optValues},{[ky]: parseInt(e.target.value)}),
							abilities:Object.assign({},{...this.state.abilities},{[ky]: st.abilities}),
							saves:Object.assign({},{...this.state.saves},{[ky]: st.saves}),
							mods:Object.assign({},{...this.state.mods},{[ky]: st.mods})});
	}
	handleAccept(e){
		var f = Object.values({...this.state.optValues});
		if(matchArray(f, this.state.rollValues)){
			var p = 0;
			for(var n in this.state.abilities){
				p=getDescriptIndex('Abilities',n);
				this.props.changeAbil(this.state.abilities[n],p);
				this.props.changeMod(this.state.mods[n],p);
				this.props.changeSaves(this.state.saves[n],p);
			}
			this.props.changeHit(HITDIE[this.props.data.Class]+this.state.mods.Constitution+ATTRIBLIB[this.props.data.Race].Hit);
			this.props.handleComplete();
		} else {
			this.setState({warning:true});
		}
		e.preventDefault();
		e.stopPropagation();
	}
	render(){
		var values = this.state.rollValues.map((x,y)=>{
			return (<div className='numHead' key={'rollValues'+y.toString()}> {x} </div>)});
		return(<section className='mainSection'>
				<fieldset><legend>Abilities</legend>
					<button onClick={this.handleRoll}>Roll</button>
					{this.state.rollValues.length>0?
						<div className='abilityInputs'>
							{[(<div className='strHead'>Stats</div>),...values]}
						</div>:null}
					{this.state.rollValues.length>0 ?
						<DistributeTable 
							data={this.props.data}
							rollValues={this.state.rollValues} 
							handleAccept={this.handleAccept}
							handleChoice={this.handleChoice}/>:null}
					<div>{this.state.warning?<div className='inputWarn'>Please select one different value for each ability.</div>:null}</div>
				</fieldset>
			</section>); 
	}
}

const getWalkSpeed=()=>{
	return ATTRIBLIB[USERDATA['Race']]['Walk'];
}
const getHitPoints=()=>{
	return HITDIE[USERDATA['Class']]+USERDATA['Modifiers']['Constitution'];
}



const AbilityResult=(props)=>{
	return props.data.Abilities.map((x,y)=>{
		return(<div className='abilityResult' key={'AbilityDisplay'+y.toString()}>
				<div className='strGrid'>{SUBJECTLIB.Abilities[y]}</div>
				<div className='numGrid'>{x}</div>
				<div className='numGrid'>{modAbility(x)}</div>
			</div>);
	});
}

class UISection3 extends React.Component{
	constructor(props){
		super(props);
		this.state={
		}
	}
	render(){
		return(<section className='mainSection'>
				<fieldset>
					<legend>Abilities</legend>
					<AbilityResult data={this.props.data}/>
				</fieldset>
			</section>);
	}
}

const SavingResult=(props)=>{
	return SUBJECTLIB.Abilities.map((x,y)=>{
		return(<div className='savingResult' key={'savingThrows'+y.toString()}>
				<div className='strGrid'>{x}</div><div className='numGrid'>{props.data['Saving Throws'][y]}</div>
			</div>);
	});
}
const SavingThrows=(props)=>{
	return(<fieldset><legend>Saves</legend>
			<SavingResult data={props.data} />
		</fieldset>);
}
const PerceptionSpeedHit=(props)=>{
	return(<fieldset><legend>Characteristics</legend>
			<div className='savingResult'><div className='strGrid'>Hit Points</div><div className='numGrid'>{props.data['Hit Points']}</div></div>
			<div className='savingResult'><div className='strGrid'>Speed</div><div className='numGrid'>{props.data['Speed']}</div></div>
		</fieldset>);
}
// ARGUMENTS: {User Class (c), User Background (b), Skill Index (n)}
const checkAble=(c,b,n)=>{
	if(SKILLLIB[c][n]&&!SKILLLIB[b][n]){
		return false;
	} else return true;
}
// ARGUMENTS: {User Proficiencey (p), User Background (b), Current Index (n)}
const applyBackToSkill=(p,b,n)=>{
	if(SKILLLIB[b][n]){
		return p;
	} else return 0;
}
// ARGUMENTS: {Skill name (s), Mod list (m)}
const applyModsToSkill=(s,m)=>{
	return m[getDescriptIndex('Abilities',MATCHSKILL[s])];
}

const respondCheckUncheck=(p,i)=>{
	if(p.hasOwnProperty(i)){return p[i];} else return 0;
}

const MakeSkillSelect=(props)=>{
	return SUBJECTLIB.Skills.map((x,y)=>{
		var w=0;
		if(props.complete){
			w=props.data.Skills[y];
		} else w= props.skills[y]+respondCheckUncheck(props.prof,x);
		var u=checkAble(props.data.Class, props.data.Background, y);
		return(<div className='skillSelect'>
				{!props.complete?<input
					type='checkBox'
					name={x}
					disabled={u}
					className='numGrid'
					onClick={props.checker}>
				</input>:<div></div>} 
				<div className='strGrid'>{x}</div>
				<div className='numGrid'>{w}</div>
			</div>)
	})
}

const Skills=(props)=>{
	return(<fieldset><legend>Skills</legend>
			{!props.complete?<div>{'Selections Remaining: '+props.useSkill.toString()}</div>:null}
			<MakeSkillSelect data={props.data} checker={props.checker} skills={props.skills} prof={props.prof} complete={props.complete} />
			{!props.complete?<button onClick={props.accept}>Accept</button>:null}
		</fieldset>);
}
class UISection4 extends React.Component{
	constructor(props){
		super(props);
		this.state={
			useSkill: 0,
			skillChoice:{},
			Skills:[],
			skillComplete:false
		}
		this.handleCheck=this.handleCheck.bind(this);
		this.handleAccept=this.handleAccept.bind(this);
	}
	componentDidMount(){
		const skillset = SUBJECTLIB.Skills.map((x,y)=>{
			return applyModsToSkill(x,this.props.data.Modifiers)+applyBackToSkill(this.props.data.Proficiency,this.props.data.Background,y)
		})
		this.setState({useSkill:USESKILL[this.props.data.Class], Skills:skillset});
	}
	handleCheck(e){
		const n=e.target.name;
		if(e.target.checked){
			this.setState(prevState=>(
				{useSkill:prevState.useSkill-=1, 
				 skillChoice:Object.assign({...prevState.skillChoice},{[n]:this.props.data.Proficiency})}));
		}
		else {
			this.setState(prevState=>({useSkill:prevState.useSkill+=1, 
				 skillChoice:Object.assign({...prevState.skillChoice},{[n]:0})}));
		}
	}
	handleAccept(e){
		if(this.state.useSkill!==0){
			// Deliver Warning
		} else{
			for(var i=0; i<this.state.Skills.length; i++){
				this.props.changeSkill(this.state.Skills[i]+(this.state.skillChoice.hasOwnProperty(SUBJECTLIB.Skills[i])?this.state.skillChoice[SUBJECTLIB.Skills[i]]:0),i);
			}
			e.target.disabled=true;
			this.setState({skillComplete:true});
			this.props.handleComplete();
		}
	}
	render(){
		return(<div>
				<PerceptionSpeedHit data={this.props.data} />
				<SavingThrows data={this.props.data} />
				<Skills 
					data={this.props.data} 
					checker={this.handleCheck} 
					skills={this.state.Skills} 
					prof={this.state.skillChoice} 
					useSkill={this.state.useSkill}
					accept={this.handleAccept}
					complete={this.state.skillComplete}/>
			</div>);
	}
}

const abilToString=(s)=>{
	return s.join(`%2D`);
}

class MainUI extends React.Component{
	constructor(props){
		super(props);
		this.state={
			uiLevel:1,
			tweetLink: TWEETPRE+'Check+out+this+character+builder!+https%3A%2F%2Fcodepen.io%2Fvtox%2Ffull%2FYjBzpz%2F'
		}
		this.completeUI=this.completeUI.bind(this);
		this.createNewTweet=this.createNewTweet.bind(this);
	}
	createNewTweet(){
		var t = TWEETPRE+'I+just+created+a+new+character%3A%0D'+
			 this.props.data.Name.replace(/\s/gi,'+')+'%0D'+
			 this.props.data.Race+'+'+this.props.data.Class+'+'+this.props.data.Background+'%0D'+
			 'stats%3A+'+abilToString(this.props.data.Abilities)+'%0D'+
			 'Try+it+at+https%3A%2F%2Fcodepen.io%2Fvtox%2Ffull%2FYjBzpz%2F';
		this.setState({tweetLink:t})
	}
	completeUI(){
		this.setState({uiLevel: this.state.uiLevel+1});
		console.log(this.state.uiLevel);
		if(this.state.uiLevel>=3){
			this.createNewTweet();
		}
	}
	render(){
		//this.props.testLog();
		return(<div><Navbar tweetLink={this.state.tweetLink}/>
			<div className={'mainContainer'}>
				{this.state.uiLevel===1?<UISection1 
					testLog={this.props.testLog}
					data={this.props.data} 
					changeNCRB={this.props.changeNCRB}
					addName={this.props.addName}
					changeClass={this.props.changeClass}
					changeRace={this.props.changeRace}
					changeBack={this.props.changeBack}
					changeSpeed={this.props.changeSpeed}
					handleComplete={this.completeUI}/>:
					<UISection1a data={this.props.data}/>}
				{this.state.uiLevel===2?
					<UISection2 
						data={this.props.data}
						handleComplete={this.completeUI}
						changeAbil={this.props.changeAbil}
						changeMod={this.props.changeMod}
						changeSaves={this.props.changeSaves}
						changeHit={this.props.changeHit}
						handleComplete={this.completeUI}/>:null}
				{this.state.uiLevel>=3?<UISection3 data={this.props.data} />:null}
				{this.state.uiLevel>=3?
					<UISection4 
						data={this.props.data} 
						changeSkill={this.props.changeSkill} 
						handleComplete={this.completeUI} />:null}
				</div></div>);
	}
}

const CHANGE_CLASS='CHANGE_CLASS';
const CHANGE_SPEED='CHANGE_SPEED';
const CHANGE_BACK='CHANGE_BACK';
const CHANGE_RACE='CHANGE_RACE';
const ADD_NAME='ADD_NAME';
const CHANGE_HIT='CHANGE_HIT';
const CHANGE_ABIL='CHANGE_ABIL';
const CHANGE_MOD='CHANGE_MOD';
const CHANGE_SKILL='CHANGE_SKILL';
const CHANGE_SAVES='CHANGE_SAVES';
const CHANGE_NCRB='CHANGE_NCRB';
const TEST_LOG='TEST_LOG';

// REDUX ACTIONS
const actions={
	changeClass:(c)=>{
		return {
			type:CHANGE_CLASS,
			payload:{'Class':c}
		}			
	},
	addName:(n)=>{
		return{
			type:ADD_NAME,
			payload:{'Name':n}
		}
	},
	changeRace:(r)=>{
		return{
			type:CHANGE_RACE,
			payload:{'Race':r}
		}
	},
	changeBack:(b)=>{
		return{
			type:CHANGE_BACK,
			payload:{'Background':b}
		}
	},
	changeSpeed:(s)=>{
		return{
			type:CHANGE_SPEED,
			payload:{'Speed':s}
		}
	},
	changeHit:(h)=>{
		return{
			type:CHANGE_HIT,
			payload:{'Hit Points':h}
		}
	},
	changeNCRB:(ncrb)=>{
		return{
			type:CHANGE_NCRB,
			payload:{...ncrb}
		}
	},
	changeAbil:(a,i)=>{
		return{
			type:CHANGE_ABIL,
			payload:{keyName:'Abilities',value:a,iter:i}
		}
	},
	changeMod:(m,i)=>{
		return{
			type:CHANGE_MOD,
			payload:{keyName:'Modifiers',value:m,iter:i}
		}
	},
	changeSkill:(s,i)=>{
		return{
			type:CHANGE_SKILL,
			payload:{keyName:'Skills',
			value:s,
			iter:i}
		}
	},
	changeSaves:(s,i)=>{
		return{
			type:CHANGE_SAVES,
			payload:{keyName:'Saving Throws',
			value:s,
			iter:i}
		}
	},
	testLog:()=>{
		console.log('ACTION CREATED');
		return{
			type:TEST_LOG,
			payload:{log: 'PAYLOAD'}
		}
	}
}

function reducer(state=[], action){
	const {type, payload}=action;
	switch(type){
		case TEST_LOG:
			console.log('REDUCER CAUGHT');
			return state;
		case CHANGE_MOD:
		case CHANGE_SKILL:
		case CHANGE_SAVES:
		case CHANGE_ABIL:
			const {keyName,value,iter}=payload;
			return {...state,[keyName]: state[keyName].slice().map((x,y)=>{
				if(y!==iter){ return x; } else return value;
			})};
		case ADD_NAME:
		case CHANGE_CLASS:
		case CHANGE_RACE:
		case CHANGE_BACK:
		case CHANGE_SPEED:
		case CHANGE_HIT:
		case CHANGE_NCRB:
			return Object.assign({},{...state},payload);
		default:
			return state;
	}
}

const RRContainer = connect(
	function mapStateToProps(state){ return { data: state }; },
	function mapDispatchToProps(dispatch){
		return bindActionCreators(actions,dispatch);
	}
)(MainUI);

const store = createStore(reducer, DataList);

// render(<Navbar />, document.getElementById('rootContainer'));
render(<Provider store={store}><RRContainer /></Provider>, document.getElementById('rootUI'));
View Compiled
Run Pen

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.4.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.1/umd/react-dom.production.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.0/redux.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js
  5. https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.2/prop-types.min.js