lila/ui/puzzle/src/view/side.ts

89 lines
3.0 KiB
TypeScript
Raw Normal View History

import sparkline from "@fnando/sparkline";
import { Controller, Puzzle, PuzzleGame, MaybeVNode } from '../interfaces';
import { dataIcon } from '../util';
import { h, thunk } from 'snabbdom';
2020-09-12 02:13:01 -06:00
import { numberFormat } from 'common/number';
import { VNode } from 'snabbdom/vnode';
2020-01-02 08:51:13 -07:00
export function puzzleBox(ctrl: Controller): VNode {
var data = ctrl.getData();
2019-02-14 21:04:24 -07:00
return h('div.puzzle__side__metas', [
puzzleInfos(ctrl, data.puzzle),
2019-02-14 21:04:24 -07:00
gameInfos(ctrl, data.game, data.puzzle)
]);
}
function puzzleInfos(ctrl: Controller, puzzle: Puzzle): VNode {
2019-02-14 21:04:24 -07:00
return h('div.infos.puzzle', {
attrs: dataIcon('-')
2019-02-14 21:04:24 -07:00
}, [h('div', [
h('a.title', {
attrs: { href: '/training/' + puzzle.id }
}, ctrl.trans('puzzleId', puzzle.id)),
2019-08-16 05:52:10 -06:00
h('p', ctrl.trans.vdom('ratingX', ctrl.vm.mode === 'play' ? h('span.hidden', ctrl.trans.noarg('hidden')) : h('strong', puzzle.rating))),
2020-09-12 02:13:01 -06:00
h('p', ctrl.trans.vdom('playedXTimes', h('strong', numberFormat(puzzle.attempts))))
2019-02-14 21:04:24 -07:00
])]);
}
function gameInfos(ctrl: Controller, game: PuzzleGame, puzzle: Puzzle): VNode {
2019-02-14 21:04:24 -07:00
return h('div.infos', {
attrs: dataIcon(game.perf.icon)
}, [h('div', [
h('p', ctrl.trans.vdom('fromGameLink', h('a', {
attrs: { href: `/${game.id}/${puzzle.color}#${puzzle.initialPly}` }
}, '#' + game.id))),
2019-02-14 21:04:24 -07:00
h('p', [
game.clock, ' • ',
game.perf.name, ' • ',
ctrl.trans.noarg(game.rated ? 'rated' : 'casual')
]),
h('div.players', game.players.map(function(p) {
return h('div.player.color-icon.is.text.' + p.color,
2019-04-08 03:19:22 -06:00
p.userId ? h('a.user-link.ulpt', {
attrs: { href: '/@/' + p.userId }
}, p.name) : p.name
);
}))
2019-02-14 21:04:24 -07:00
])]);
}
2020-01-02 08:51:13 -07:00
export function userBox(ctrl: Controller): MaybeVNode {
2017-07-03 06:30:50 -06:00
const data = ctrl.getData();
if (!data.user) return;
const diff = ctrl.vm.round && ctrl.vm.round.ratingDiff;
2017-07-03 06:23:08 -06:00
const hash = ctrl.recentHash();
2019-02-14 21:04:24 -07:00
return h('div.puzzle__side__user', [
h('h2', ctrl.trans.vdom('yourPuzzleRatingX', h('strong', [
data.user.rating,
2020-02-23 18:21:27 -07:00
...(diff >= 0 ? [' ', h('good.rp', '+' + diff)] : []),
...(diff < 0 ? [' ', h('bad.rp', '' + (-diff))] : [])
]))),
2017-07-03 06:23:08 -06:00
h('div', thunk('div.rating_chart.' + hash, ratingChart, [ctrl, hash]))
]);
}
2020-01-02 08:51:13 -07:00
function ratingChart(ctrl: Controller, hash: string): VNode {
2017-07-03 06:23:08 -06:00
return h('div.rating_chart.' + hash, {
hook: {
insert(vnode) { drawRatingChart(ctrl, vnode) },
postpatch(_, vnode) { drawRatingChart(ctrl, vnode) }
}
});
}
2020-01-02 08:51:13 -07:00
function drawRatingChart(ctrl: Controller, vnode: VNode): void {
2019-02-14 21:04:24 -07:00
const $el = $(vnode.elm as HTMLElement);
const points = ctrl.getData().user!.recent.map(r => r[2] + r[1]);
const localPuzzleMin = Math.min(...points);
const redraw = () => {
const $svg = $('<svg class="sparkline" height="80px" stroke-width="3">')
.attr('width', Math.round($el.outerWidth()) + 'px')
.prependTo($(vnode.elm).empty());
sparkline($svg[0] as unknown as SVGSVGElement, points.map(r => r - localPuzzleMin), {
interactive: true,
})
};
2020-07-01 06:19:16 -06:00
requestAnimationFrame(redraw);
2019-02-15 02:12:19 -07:00
window.addEventListener('resize', redraw);
}