Merge pull request #151 from esSteres/leagues
fix numerical inputs in league options
This commit is contained in:
commit
121d92885a
|
@ -1,4 +1,5 @@
|
|||
import React, {useState, useRef, useLayoutEffect, useReducer} from 'react';
|
||||
import {removeIndex, replaceIndex, append, arrayOf, shallowClone, getUID, DistributiveOmit} from './util';
|
||||
import './CreateLeague.css';
|
||||
import twemoji from 'twemoji';
|
||||
|
||||
|
@ -46,11 +47,6 @@ class TeamState {
|
|||
}
|
||||
}
|
||||
|
||||
let getUID = function() { // does NOT generate UUIDs. Meant to create list keys ONLY
|
||||
let id = 0;
|
||||
return function() { return id++ }
|
||||
}()
|
||||
|
||||
// STRUCTURE REDUCER
|
||||
|
||||
type StructureReducerActions =
|
||||
|
@ -168,35 +164,6 @@ function LeagueOptionsReducer(state: LeagueOptionsState, action: OptionsReducerA
|
|||
return newState
|
||||
}
|
||||
|
||||
// UTIL
|
||||
|
||||
function removeIndex(arr: any[], index: number) {
|
||||
return arr.slice(0, index).concat(arr.slice(index+1));
|
||||
}
|
||||
|
||||
function replaceIndex<T>(arr: T[], index: number, val: T) {
|
||||
return arr.slice(0, index).concat([val]).concat(arr.slice(index+1));
|
||||
}
|
||||
|
||||
function append<T>(arr: T[], val: T) {
|
||||
return arr.concat([val]);
|
||||
}
|
||||
|
||||
function arrayOf<T>(length: number, func: (i: number) => T): T[] {
|
||||
var out: T[] = [];
|
||||
for (var i = 0; i < length; i++) {
|
||||
out.push(func(i));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function shallowClone<T>(obj: T): T {
|
||||
return Object.assign({}, obj);
|
||||
}
|
||||
|
||||
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
|
||||
type DistributivePick<T, K extends keyof T> = T extends any ? Pick<T, K> : never;
|
||||
|
||||
// CREATE LEAGUE
|
||||
|
||||
let initLeagueStructure = {
|
||||
|
@ -323,7 +290,7 @@ function validRequest(name:string, structure: LeagueStructureState, options:Leag
|
|||
}
|
||||
|
||||
function validNumber(value: string, min = 1) {
|
||||
return Number(value) !== NaN && Number(value) >= min
|
||||
return !isNaN(Number(value)) && Number(value) >= min
|
||||
}
|
||||
|
||||
// LEAGUE STRUCUTRE
|
||||
|
@ -464,7 +431,7 @@ function LeagueOptions(props: {state: LeagueOptionsState, dispatch: React.Dispat
|
|||
props.dispatch({type: 'set_games_series', value: value})} showError={props.showError}/>
|
||||
<NumberInput title="Number of teams from top of division to postseason" value={props.state.top_postseason} setValue={(value: string) =>
|
||||
props.dispatch({type: 'set_top_postseason', value: value})} showError={props.showError}/>
|
||||
<NumberInput title="Number of wildcards" value={props.state.wildcards} setValue={(value: string) =>
|
||||
<NumberInput title="Number of wildcards" value={props.state.wildcards} minValue={0} setValue={(value: string) =>
|
||||
props.dispatch({type: 'set_wildcards', value: value})} showError={props.showError}/>
|
||||
</div>
|
||||
<div className="cl_option_column">
|
||||
|
@ -479,12 +446,16 @@ function LeagueOptions(props: {state: LeagueOptionsState, dispatch: React.Dispat
|
|||
);
|
||||
}
|
||||
|
||||
function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean}) {
|
||||
function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean, minValue?:number}) {
|
||||
let minValue = 1;
|
||||
if (props.minValue !== undefined) {
|
||||
minValue = props.minValue
|
||||
}
|
||||
return (
|
||||
<div className="cl_option_box">
|
||||
<div className="cl_option_label">{props.title}</div>
|
||||
<input className="cl_option_input" type="number" min="0" value={props.value} onChange={e => props.setValue(e.target.value)}/>
|
||||
<div className="cl_option_err">{(Number(props.value) === NaN || Number(props.value) < 0) && props.showError ? "Must be a number greater than 0" : ""}</div>
|
||||
<input className="cl_option_input" type="number" min={minValue} value={props.value} onChange={e => props.setValue(e.target.value)}/>
|
||||
<div className="cl_option_err">{(!isNaN(Number(props.value)) || Number(props.value) < minValue) && props.showError ? "Must be a number greater than "+minValue : ""}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
.game {
|
||||
align-self: stretch;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -3,5 +3,35 @@
|
|||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.history_box {
|
||||
width: 100%;
|
||||
margin-top: 3rem;
|
||||
padding: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
background: var(--background-main);
|
||||
border-radius: 0.25rem;
|
||||
width: 100%;
|
||||
min-width: 32rem;
|
||||
max-width: 44rem;
|
||||
box-sizing: border-box;
|
||||
border: 4px solid;
|
||||
border-radius: 4px;
|
||||
border-color: var(--highlight);
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.history_title {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.history_update {
|
||||
height: 4rem;
|
||||
margin: 0.5rem;
|
||||
}
|
|
@ -1,22 +1,62 @@
|
|||
import React, {useState} from 'react';
|
||||
import React, {useState, useRef, useLayoutEffect} from 'react';
|
||||
import twemoji from 'twemoji';
|
||||
import ReactRouter from 'react-router';
|
||||
import {GameState, useListener} from './GamesUtil';
|
||||
import './GamePage.css';
|
||||
import Game from './Game';
|
||||
import {getUID} from './util';
|
||||
|
||||
function GamePage(props: ReactRouter.RouteComponentProps<{id: string}>) {
|
||||
let [games, setGames] = useState<[string, GameState][]>([]);
|
||||
useListener((newGames) => setGames(newGames));
|
||||
let [game, setGame] = useState<[string, GameState]|undefined>(undefined);
|
||||
let history = useRef<[number, string, string][]>([]);
|
||||
|
||||
useListener((newGames) => {
|
||||
let newGame = newGames.find((gamePair) => gamePair[0] === props.match.params.id);
|
||||
setGame(newGame);
|
||||
console.log(newGame);
|
||||
if (newGame !== undefined && newGame[1].start_delay < 0 && newGame[1].end_delay > 8) {
|
||||
history.current.unshift([getUID(), newGame[1].update_emoji, newGame[1].update_text]);
|
||||
if (history.current.length > 8) {
|
||||
history.current.pop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (game === undefined) {
|
||||
return <div id="game_container">The game you're looking for either doesn't exist or has already ended.</div>
|
||||
}
|
||||
|
||||
let game = games.find((game) => game[0] === props.match.params.id)
|
||||
return (
|
||||
<div id="game_container">
|
||||
{ game ?
|
||||
<Game gameId={game[0]} state={game[1]}/> :
|
||||
"The game you're looking for either doesn't exist or has already ended."
|
||||
<Game gameId={game[0]} state={game[1]}/>
|
||||
{ history.current.length > 0 ?
|
||||
<GameHistory history={history.current}/> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GameHistory(props: {history: [number, string, string][]}) {
|
||||
let self = useRef<HTMLDivElement>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (self.current) {
|
||||
twemoji.parse(self.current);
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="history_box" ref={self}>
|
||||
<div className="history_title">History</div>
|
||||
{props.history.map((update) => (
|
||||
<div className="update history_update" key={update[0]}>
|
||||
<div className="update_emoji">{update[1]}</div>
|
||||
<div className="update_text">{update[2]}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GamePage;
|
|
@ -76,9 +76,4 @@
|
|||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
.emptyslot {
|
||||
border: none;
|
||||
min-height: 0px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ function GamesPage() {
|
|||
let [search, setSearch] = useState(window.location.search);
|
||||
useEffect(() => {
|
||||
setSearch(window.location.search);
|
||||
//eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [window.location.search])
|
||||
|
||||
let searchparams = new URLSearchParams(search);
|
||||
|
|
|
@ -20,6 +20,8 @@ interface GameState {
|
|||
update_text: string
|
||||
is_league: boolean
|
||||
leagueoruser: string
|
||||
start_delay: number
|
||||
end_delay: number
|
||||
}
|
||||
|
||||
type GameList = ([id: string, game: GameState] | null)[];
|
||||
|
@ -32,6 +34,7 @@ const useListener = (onUpdate: (update: [string, GameState][]) => void, url: str
|
|||
socket.on('connect', () => socket.emit('recieved', {}));
|
||||
socket.on('states_update', onUpdate);
|
||||
return () => {socket.disconnect()};
|
||||
//eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [url])
|
||||
}
|
||||
|
||||
|
|
|
@ -32,14 +32,14 @@ function Header() {
|
|||
<div id="link_div">
|
||||
<a href="https://www.patreon.com/sixteen" className="patreon_link" target="_blank" rel="noopener noreferrer">
|
||||
<div className="patreon_container">
|
||||
<img className="patreon_logo" src={patreonLogo} />
|
||||
<img className="patreon_logo" src={patreonLogo} alt="Patreon"/>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://github.com/Sakimori/matteo-the-prestige" className="github_link" target="_blank" rel="noopener noreferrer">
|
||||
<img className="github_logo" src={githubLogo} />
|
||||
<img className="github_logo" src={githubLogo} alt="Github"/>
|
||||
</a>
|
||||
<a href="https://twitter.com/intent/follow?screen_name=SIBR_XVI" className="twitter_link" target="_blank" rel="noopener noreferrer">
|
||||
<img className="twitter_logo" src={twitterLogo} />
|
||||
<img className="twitter_logo" src={twitterLogo} alt="Twitter"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="utility_links">
|
||||
|
|
37
simmadome/src/util.tsx
Normal file
37
simmadome/src/util.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import {useRef} from 'react';
|
||||
|
||||
|
||||
function removeIndex(arr: any[], index: number) {
|
||||
return arr.slice(0, index).concat(arr.slice(index+1));
|
||||
}
|
||||
|
||||
function replaceIndex<T>(arr: T[], index: number, val: T) {
|
||||
return arr.slice(0, index).concat([val]).concat(arr.slice(index+1));
|
||||
}
|
||||
|
||||
function append<T>(arr: T[], val: T) {
|
||||
return arr.concat([val]);
|
||||
}
|
||||
|
||||
function arrayOf<T>(length: number, func: (i: number) => T): T[] {
|
||||
var out: T[] = [];
|
||||
for (var i = 0; i < length; i++) {
|
||||
out.push(func(i));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function shallowClone<T>(obj: T): T {
|
||||
return Object.assign({}, obj);
|
||||
}
|
||||
|
||||
let getUID = function() { // does NOT generate UUIDs. Meant to create list keys ONLY
|
||||
let id = 0;
|
||||
return function() { return id++ }
|
||||
}()
|
||||
|
||||
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
|
||||
//type DistributivePick<T, K extends keyof T> = T extends any ? Pick<T, K> : never;
|
||||
|
||||
export {removeIndex, replaceIndex, append, arrayOf, shallowClone, getUID};
|
||||
export type {DistributiveOmit};
|
Loading…
Reference in New Issue
Block a user