lila/ui/tournament/src/view/battle.ts

130 lines
3.8 KiB
TypeScript

import TournamentController from '../ctrl';
import { bind, onInsert, playerName } from './util';
import { h } from 'snabbdom'
import { TeamBattle, RankedTeam, TournamentData, MaybeVNode } from '../interfaces';
import { VNode } from 'snabbdom/vnode';
export function joinWithTeamSelector(ctrl: TournamentController) {
const onClose = () => {
ctrl.joinWithTeamSelector = false;
ctrl.redraw();
};
const tb = ctrl.data.teamBattle!;
return h('div#modal-overlay', {
hook: bind('click', onClose)
}, [
h('div#modal-wrap.team-battle__choice', {
hook: onInsert(el => {
el.addEventListener('click', e => e.stopPropagation());
})
}, [
h('span.close', {
attrs: { 'data-icon': 'L' },
hook: bind('click', onClose)
}),
h('div.team-picker', [
h('h2', "Pick your team"),
h('br'),
...(tb.joinWith.length ? [
h('p', "Which team will you represent in this battle?"),
...tb.joinWith.map(id => h('a.button', {
hook: bind('click', () => ctrl.join(undefined, id), ctrl.redraw)
}, tb.teams[id]))
] : [
h('p', "You must join one of these teams to participate!"),
h('ul', shuffleArray(Object.keys(tb.teams)).map((t: string) =>
h('li', h('a', {
attrs: { href: '/team/' + t }
}, tb.teams[t]))
))
])
])
])
]);
}
export function teamStanding(ctrl: TournamentController, klass?: string): VNode | null {
const battle = ctrl.data.teamBattle,
standing = ctrl.data.teamStanding,
bigBattle = battle && Object.keys(battle.teams).length > 10;
return battle && standing ? h('table.slist.tour__team-standing' + (klass ? '.' + klass : ''), [
h('tbody', [
...standing.map(rt => teamTr(ctrl, battle, rt)),
...(bigBattle ? [
extraTeams(ctrl.data),
myTeam(ctrl, battle)
] : [])
])
]) : null;
}
function extraTeams(tour: TournamentData): VNode {
return h('tr',
h('td.more-teams', {
attrs: { colspan: 4 }
}, h('a', {
attrs: {
href: `/tournament/${tour.id}/teams`
},
}, 'View all teams')
)
);
}
function myTeam(ctrl: TournamentController, battle: TeamBattle): MaybeVNode {
const team = ctrl.data.myTeam;
return team && team.rank > 10 ? teamTr(ctrl, battle, team) : undefined;
}
export function teamName(battle: TeamBattle, teamId: string): VNode {
return h('team.ttc-' + Object.keys(battle.teams).indexOf(teamId), battle.teams[teamId]);
}
function teamTr(ctrl: TournamentController, battle: TeamBattle, team: RankedTeam) {
const players = [] as (string | VNode)[];
team.players.forEach((p, i) => {
if (i > 0) players.push('+');
players.push(h('score.ulpt.user-link', {
key: p.user.name,
class: { top: i === 0 },
attrs: {
'data-href': '/@/' + p.user.name,
'data-name': p.user.name
},
hook: {
destroy: vnode => $.powerTip.destroy(vnode.elm as HTMLElement),
...bind('click', _ => ctrl.jumpToPageOf(p.user.name), ctrl.redraw)
}
}, [
...(i === 0 ? [h('username', playerName(p.user)), ' '] : []),
'' + p.score
]));
});
return h('tr', {
key: team.id,
class: {
active: ctrl.teamInfo.requested == team.id
},
hook: bind('click', _ => ctrl.showTeamInfo(team.id), ctrl.redraw)
}, [
h('td.rank', '' + team.rank),
h('td.team', [
teamName(battle, team.id)
]),
h('td.players', players),
h('td.total', [
h('strong', '' + team.score)
])
]);
}
/* Randomize array element order in-place. Using Durstenfeld shuffle algorithm. */
function shuffleArray<A>(array: A[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}