diff --git a/simmadome/src/CreateLeague.css b/simmadome/src/CreateLeague.css new file mode 100644 index 0000000..e867503 --- /dev/null +++ b/simmadome/src/CreateLeague.css @@ -0,0 +1,177 @@ +th, td { + border: none; + padding: 0; + height: 100%; +} + +table { + border-collapse: collapse; + height: min-content; +} + +th .cl_subleague_bg { + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; +} + +tbody tr:last-child .cl_subleague_bg { + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} + +input { + border: none; + border-radius: 1rem; + height: 2rem; + padding-left: 1rem; + background: var(--background-secondary); + font-size: 14pt; +} + +.cl_league_main { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + margin-top: 3rem; +} + +.cl_league_name { + margin: 1rem; +} + +.cl_league_options, .cl_league_structure { + display: flex; + background: var(--background-tertiary); + flex-direction: column; + max-width: 100%; + border-radius: 1rem; + padding-top: 1.5rem; +} + +.cl_league_structure, .cl_subleague_add_align { + display: flex; + align-items: center; + justify-content: center; + width: min-content; +} + +.cl_league_structure_table { + margin: 1rem; + margin-left: 0rem; + margin-top: 0rem; +} + +.cl_league_structure_scrollbox { + max-width: 100%; + overflow-y: scroll; +} + + /* Hide scrollbar for Chrome, Safari and Opera */ +.cl_league_structure_scrollbox::-webkit-scrollbar { + display: none; +} + +/* Hide scrollbar for IE, Edge and Firefox */ +.cl_league_structure_scrollbox { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.cl_subleague_add_align{ + margin-left: 1.5rem; + padding-right: 1.5rem; +} + +.cl_subleague_header { + display: flex; + width:100%; + align-items: center; + justify-content: space-between; +} + +.cl_subleague_bg { + background: var(--background-main); + padding:1rem; + padding-bottom: 0rem; + margin: 0rem 0.5rem; + height: 100%; + width: 20rem; +} + +.cl_subleague_name, .cl_newteam_name { + flex-grow: 1; + margin-right: 0.5rem; +} + +.cl_division_name { + margin-bottom: 0.5rem; + width: 95%; +} + +.cl_division { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 1rem; + border-radius: 0.5rem; + background: var(--background-accent); +} + +.cl_team, .cl_team_add { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin: 0.25rem 0rem; +} + +.cl_team_name { + font-size: 14pt; + padding-right: 0.5rem; + overflow: hidden; + text-overflow: ellipsis; +} + +/* button styles */ + +button > .emoji { + margin: 0; +} + +.cl_subleague_delete, .cl_team_delete, .cl_division_delete, .cl_subleague_add, .cl_division_add, .cl_newteam_add { + padding: 0; + width: 2rem; + height: 2rem; + border: none; + border-radius: 1rem; + display: flex; + align-items: center; + justify-content: center; +} + +.cl_subleague_delete, .cl_team_delete, .cl_division_delete { + background: var(--accent-red); +} + +.cl_subleague_add, .cl_division_add, .cl_newteam_add { + background: var(--accent-green); +} + +.cl_subleague_add { + margin-top: 3rem; +} + +.cl_division_add { + margin: 1.25rem; +} + +.cl_division_delete { + margin-right: 1rem; + margin-top: 1rem; +} + +.cl_delete_filler { + min-width: 3rem; +} \ No newline at end of file diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx new file mode 100644 index 0000000..a64c4d8 --- /dev/null +++ b/simmadome/src/CreateLeague.tsx @@ -0,0 +1,250 @@ +import React, {useState, useRef, useLayoutEffect, useReducer} from 'react'; +import './CreateLeague.css'; +import twemoji from 'twemoji'; + +interface LeagueStructureState { + subleagues: SubleagueState[] +} + +interface SubleagueState { + name: string + divisions: DivisionState[] +} + +interface DivisionState { + name: string + teams: TeamState[] +} + +interface TeamState { + name: string +} + +let initLeagueStructure = { + subleagues: [0, 1].map((val) => ({ + name: "", + divisions: [0, 1].map((val) => ({ + name: "", + teams: [] + })) + })) +} + +type LeagueReducerActions = + {type: 'remove_subleague', subleague_index: number} | + {type: 'add_subleague'} | + {type: 'rename_subleague', subleague_index: number, name: string} | + {type: 'remove_divisions', division_index: number} | + {type: 'add_divisions'} | + {type: 'rename_division', subleague_index: number, division_index: number, name: string} | + {type: 'remove_team', subleague_index: number, division_index: number, name:string} | + {type: 'add_team', subleague_index:number, division_index:number, name:string} + +type DistributiveOmit = T extends any ? Omit : never; + +function leagueStructureReducer(state: LeagueStructureState, action: LeagueReducerActions): LeagueStructureState { + switch (action.type) { + case 'remove_subleague': + return {subleagues: removeIndex(state.subleagues, action.subleague_index)}; + case 'add_subleague': + return {subleagues: state.subleagues.concat([{ + name: "", + divisions: arrayOf(state.subleagues[0].divisions.length, i => ({ + name: "", + teams: [] + })) + }])}; + case 'rename_subleague': + return replaceSubleague(state, action.subleague_index, subleague => ({ + name: action.name, + divisions: subleague.divisions + })); + case 'remove_divisions': + return {subleagues: state.subleagues.map(subleague => ({ + name: subleague.name, + divisions: removeIndex(subleague.divisions, action.division_index) + }))}; + case 'add_divisions': + return {subleagues: state.subleagues.map(subleague => ({ + name: subleague.name, + divisions: subleague.divisions.concat([{name: "", teams: []}]) + }))}; + case 'rename_division': + return replaceDivision(state, action.subleague_index, action.division_index, division => ({ + name: action.name, + teams: division.teams + })); + case 'remove_team': + return replaceDivision(state, action.subleague_index, action.division_index, division => ({ + name: division.name, + teams: removeIndex(division.teams, division.teams.findIndex(val => val.name === action.name)) + })); + case 'add_team': + return replaceDivision(state, action.subleague_index, action.division_index, division => ({ + name: division.name, + teams: division.teams.concat([{name: action.name}]) + })); + } +} + +function replaceSubleague(state: LeagueStructureState, si: number, func: (val: SubleagueState) => SubleagueState) { + return {subleagues: replaceIndex(state.subleagues, si, func(state.subleagues[si]))} +} + +function replaceDivision(state: LeagueStructureState, si: number, di: number, func:(val: DivisionState) => DivisionState) { + return replaceSubleague(state, si, subleague => ({ + name: subleague.name, + divisions: replaceIndex(subleague.divisions, di, func(subleague.divisions[di])) + })) +} + +function removeIndex(arr: any[], index: number) { + return arr.slice(0, index).concat(arr.slice(index+1)); +} + +function replaceIndex(arr: T[], index: number, val: T) { + return arr.slice(0, index).concat([val]).concat(arr.slice(index+1)); +} + +function append(arr: T[], val: T) { + return arr.concat([val]); +} + +function arrayOf(length: number, func: (i: number) => T): T[] { + var out: T[] = []; + for (var i = 0; i < length; i++) { + out.push(func(i)); + } + return out; +} + +function CreateLeague() { + let [name, setName] = useState(""); + let [structure, dispatch] = useReducer(leagueStructureReducer, initLeagueStructure); + + let self = useRef(null) + + useLayoutEffect(() => { + if (self.current) { + twemoji.parse(self.current) + } + }) + + return ( +
+ setName(e.target.value)}/> + + +
+ ); +} + +function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch}) { + return ( +
+
+
+ + + +
+ +
+
+ +
+ ); +} + +function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { + return ( + + + + {props.subleagues.map((subleague, i) => ( + +
+ 1} dispatch={action => + props.dispatch(Object.assign({subleague_index: i}, action)) + }/> +
+ + ))} + + + ); +} + +function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispatch:(action: DistributiveOmit) => void}) { + return ( +
+ + props.dispatch({type: 'rename_subleague', name: e.target.value}) + }/> + {props.canDelete ? : null} +
+ ); +} + +function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { + return ( + + {props.subleagues[0].divisions.map((val, di) => ( + + + {props.subleagues[0].divisions.length > 1 ? + : + null + } + + {props.subleagues.map((subleague, si) => ( + +
+ + props.dispatch(Object.assign({subleague_index: si, division_index: di}, action)) + }/> +
+ + ))} + + ))} + + ); +} + +function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit) => void}) { + let [newName, setNewName] = useState(""); + + return ( +
+ + props.dispatch({type: 'rename_division', name: e.target.value}) + }/> + {props.state.teams.map((team, i) => ( +
+
{team.name}
+ +
+ ))} +
+ setNewName(e.target.value)}/> + +
+
+ ); +} + +function LeagueOptions() { + return ( +
+
+ +
+
+ ); +} + +export default CreateLeague; \ No newline at end of file diff --git a/simmadome/src/index.tsx b/simmadome/src/index.tsx index 1235dcc..21bd0f7 100644 --- a/simmadome/src/index.tsx +++ b/simmadome/src/index.tsx @@ -4,6 +4,7 @@ import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import './index.css'; import GamesPage from './GamesPage'; import GamePage from './GamePage'; +import CreateLeague from './CreateLeague'; import discordlogo from "./img/discord.png"; import reportWebVitals from './reportWebVitals'; @@ -13,6 +14,7 @@ ReactDOM.render(
+