refactor ui modals - after #9380
parent
219e5d5b82
commit
c801cd46d3
|
@ -30,25 +30,25 @@ lichess.studyTourChapter = function (study) {
|
|||
'A study can have several chapters.<br>' +
|
||||
'Each chapter has a distinct move tree,<br>' +
|
||||
'and can be created in various ways.',
|
||||
attachTo: '.study__modal label[for=chapter-name] left',
|
||||
attachTo: '#modal-wrap label[for=chapter-name] left',
|
||||
},
|
||||
{
|
||||
title: 'From initial position',
|
||||
text: 'Just a board setup for a new game.<br>' + 'Suited to explore openings.',
|
||||
attachTo: '.study__modal .tabs-horiz .init top',
|
||||
attachTo: '#modal-wrap .tabs-horiz .init top',
|
||||
when: onTab('init'),
|
||||
},
|
||||
{
|
||||
title: 'Custom position',
|
||||
text: 'Setup the board your way.<br>' + 'Suited to explore endgames.',
|
||||
attachTo: '.study__modal .tabs-horiz .edit bottom',
|
||||
attachTo: '#modal-wrap .tabs-horiz .edit bottom',
|
||||
when: onTab('edit'),
|
||||
},
|
||||
{
|
||||
title: 'Load an existing lichess game',
|
||||
text:
|
||||
'Paste a lichess game URL<br>' + '(like lichess.org/7fHIU0XI)<br>' + 'to load the game moves in the chapter.',
|
||||
attachTo: '.study__modal .tabs-horiz .game top',
|
||||
attachTo: '#modal-wrap .tabs-horiz .game top',
|
||||
when: onTab('game'),
|
||||
},
|
||||
{
|
||||
|
@ -57,19 +57,19 @@ lichess.studyTourChapter = function (study) {
|
|||
'Paste a position in FEN format<br>' +
|
||||
'<i>4k3/4rb2/8/7p/8/5Q2/1PP5/1K6 w</i><br>' +
|
||||
'to start the chapter from a position.',
|
||||
attachTo: '.study__modal .tabs-horiz .fen top',
|
||||
attachTo: '#modal-wrap .tabs-horiz .fen top',
|
||||
when: onTab('fen'),
|
||||
},
|
||||
{
|
||||
title: 'From a PGN game',
|
||||
text: 'Paste a game in PGN format.<br>' + 'to load moves, comments and variations in the chapter.',
|
||||
attachTo: '.study__modal .tabs-horiz .pgn top',
|
||||
attachTo: '#modal-wrap .tabs-horiz .pgn top',
|
||||
when: onTab('pgn'),
|
||||
},
|
||||
{
|
||||
title: 'Studies support variants',
|
||||
text: 'Yes, you can study crazyhouse,<br>' + 'and all lichess variants!',
|
||||
attachTo: '.study__modal label[for=chapter-variant] left',
|
||||
attachTo: '#modal-wrap label[for=chapter-variant] left',
|
||||
when: onTab('init'),
|
||||
},
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ lichess.studyTourChapter = function (study) {
|
|||
action: tour.next,
|
||||
},
|
||||
],
|
||||
attachTo: '.study__modal .help bottom',
|
||||
attachTo: '#modal-wrap .help bottom',
|
||||
},
|
||||
].forEach(function (s) {
|
||||
tour.addStep(s.title, s);
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
.study__modal {
|
||||
&#modal-wrap {
|
||||
padding: 1rem;
|
||||
min-width: 80vw;
|
||||
#modal-wrap {
|
||||
padding: 1rem;
|
||||
min-width: 80vw;
|
||||
|
||||
@include breakpoint($mq-x-small) {
|
||||
min-width: 500px;
|
||||
}
|
||||
@include breakpoint($mq-x-small) {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
> div {
|
||||
|
|
|
@ -200,7 +200,11 @@ export function view(ctrl: AnalyseCtrl): VNode {
|
|||
? h(
|
||||
'a.button.button-empty',
|
||||
{
|
||||
hook: bind('click', _ => modal($('.continue-with.g_' + d.game.id))),
|
||||
hook: bind('click', _ =>
|
||||
modal({
|
||||
content: $('.continue-with.g_' + d.game.id),
|
||||
})
|
||||
),
|
||||
attrs: dataIcon(''),
|
||||
},
|
||||
noarg('continueFromHere')
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as control from './control';
|
|||
import * as xhr from 'common/xhr';
|
||||
import AnalyseCtrl from './ctrl';
|
||||
import { h, VNode } from 'snabbdom';
|
||||
import { modal } from './modal';
|
||||
import { snabModal } from 'common/modal';
|
||||
import { spinner } from './util';
|
||||
|
||||
export const bind = (ctrl: AnalyseCtrl) => {
|
||||
|
@ -93,12 +93,12 @@ export const bind = (ctrl: AnalyseCtrl) => {
|
|||
};
|
||||
|
||||
export function view(ctrl: AnalyseCtrl): VNode {
|
||||
return modal({
|
||||
return snabModal({
|
||||
class: 'keyboard-help',
|
||||
onInsert(el: HTMLElement) {
|
||||
onInsert($wrap: Cash) {
|
||||
lichess.loadCssPath('analyse.keyboard');
|
||||
xhr.text(xhr.url('/analysis/help', { study: !!ctrl.study })).then(html => {
|
||||
el.querySelector('.scrollable')!.innerHTML = html;
|
||||
$wrap.find('.scrollable').html(html);
|
||||
});
|
||||
},
|
||||
onClose() {
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import { h, VNode } from 'snabbdom';
|
||||
import { focusFirstChild } from 'common/modal';
|
||||
import { MaybeVNodes } from './interfaces';
|
||||
import { bind, onInsert } from './util';
|
||||
|
||||
interface Modal {
|
||||
class?: string;
|
||||
content: MaybeVNodes;
|
||||
onInsert?: (el: HTMLElement) => void;
|
||||
onClose(): void;
|
||||
noClickAway?: boolean;
|
||||
}
|
||||
|
||||
export function modal(d: Modal): VNode {
|
||||
return h(
|
||||
'div#modal-overlay',
|
||||
{
|
||||
...(!d.noClickAway ? { hook: bind('mousedown', d.onClose) } : {}),
|
||||
},
|
||||
[
|
||||
h(
|
||||
'div#modal-wrap.study__modal.' + d.class,
|
||||
{
|
||||
hook: onInsert(el => {
|
||||
focusFirstChild($(el));
|
||||
el.addEventListener('mousedown', e => e.stopPropagation());
|
||||
d.onInsert && d.onInsert(el);
|
||||
}),
|
||||
},
|
||||
[
|
||||
h('span.close', {
|
||||
attrs: {
|
||||
'data-icon': '',
|
||||
role: 'button',
|
||||
'aria-label': 'Close',
|
||||
tabindex: '0',
|
||||
},
|
||||
hook: onInsert(el => {
|
||||
el.addEventListener('click', d.onClose);
|
||||
el.addEventListener('keydown', e => (e.code === 'Enter' || e.code === 'Space' ? d.onClose() : true));
|
||||
}),
|
||||
}),
|
||||
h('div', d.content),
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
export function button(name: string): VNode {
|
||||
return h(
|
||||
'div.form-actions.single',
|
||||
h(
|
||||
'button.button',
|
||||
{
|
||||
attrs: { type: 'submit' },
|
||||
},
|
||||
name
|
||||
)
|
||||
);
|
||||
}
|
|
@ -136,8 +136,8 @@ export default function (element: HTMLElement, ctrl: AnalyseCtrl) {
|
|||
$panels.on('click', '.embed-howto', function (this: HTMLElement) {
|
||||
const url = `${baseUrl()}/embed/${data.game.id}${location.hash}`;
|
||||
const iframe = '<iframe src="' + url + '?theme=auto&bg=auto"\nwidth=600 height=397 frameborder=0></iframe>';
|
||||
modal(
|
||||
$(
|
||||
modal({
|
||||
content: $(
|
||||
'<strong style="font-size:1.5em">' +
|
||||
$(this).html() +
|
||||
'</strong><br /><br />' +
|
||||
|
@ -147,7 +147,7 @@ export default function (element: HTMLElement, ctrl: AnalyseCtrl) {
|
|||
iframe +
|
||||
'<br /><br />' +
|
||||
'<a class="text" data-icon="" href="/developers#embed-game">Read more about embedding games</a>'
|
||||
)
|
||||
);
|
||||
),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { h, VNode } from 'snabbdom';
|
||||
import { defined, prop, Prop } from 'common';
|
||||
import { Redraw } from '../interfaces';
|
||||
import { bind, bindSubmit, spinner, option, onInsert, emptyRedButton } from '../util';
|
||||
import * as modal from '../modal';
|
||||
import * as chapterForm from './chapterNewForm';
|
||||
import { bind, bindSubmit, spinner, option, onInsert, emptyRedButton } from '../util';
|
||||
import { ChapterMode, EditChapterData, Orientation, StudyChapterConfig, StudyChapterMeta } from './interfaces';
|
||||
import { defined, prop, Prop } from 'common';
|
||||
import { h, VNode } from 'snabbdom';
|
||||
import { modalButton } from './chapterNewForm';
|
||||
import { Redraw } from '../interfaces';
|
||||
import { snabModal } from 'common/modal';
|
||||
import { StudySocketSend } from '../socket';
|
||||
|
||||
export interface StudyChapterEditFormCtrl {
|
||||
|
@ -75,7 +76,7 @@ export function ctrl(
|
|||
export function view(ctrl: StudyChapterEditFormCtrl): VNode | undefined {
|
||||
const data = ctrl.current();
|
||||
return data
|
||||
? modal.modal({
|
||||
? snabModal({
|
||||
class: 'edit-' + data.id, // full redraw when changing chapter
|
||||
onClose() {
|
||||
ctrl.current(null);
|
||||
|
@ -201,6 +202,6 @@ function viewLoaded(ctrl: StudyChapterEditFormCtrl, data: StudyChapterConfig): V
|
|||
].map(v => option(v[0], data.description ? '1' : '', v[1]))
|
||||
),
|
||||
]),
|
||||
modal.button(ctrl.trans.noarg('saveChapter')),
|
||||
modalButton(ctrl.trans.noarg('saveChapter')),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { h, VNode } from 'snabbdom';
|
||||
import { defined, prop, Prop } from 'common';
|
||||
import { storedProp, StoredProp } from 'common/storage';
|
||||
import * as xhr from 'common/xhr';
|
||||
import AnalyseCtrl from '../ctrl';
|
||||
import { bind, bindSubmit, spinner, option, onInsert } from '../util';
|
||||
import { variants as xhrVariants, importPgn } from './studyXhr';
|
||||
import * as modal from '../modal';
|
||||
import { chapter as chapterTour } from './studyTour';
|
||||
import { ChapterData, ChapterMode, Orientation, StudyChapterMeta } from './interfaces';
|
||||
import { Redraw } from '../interfaces';
|
||||
import AnalyseCtrl from '../ctrl';
|
||||
import { StudySocketSend } from '../socket';
|
||||
import { defined, prop, Prop } from 'common';
|
||||
import { h, VNode } from 'snabbdom';
|
||||
import { parseFen } from 'chessops/fen';
|
||||
import { Redraw } from '../interfaces';
|
||||
import { snabModal } from 'common/modal';
|
||||
import { storedProp, StoredProp } from 'common/storage';
|
||||
import { StudySocketSend } from '../socket';
|
||||
import { variants as xhrVariants, importPgn } from './studyXhr';
|
||||
|
||||
export const modeChoices = [
|
||||
['normal', 'normalAnalysis'],
|
||||
|
@ -140,7 +140,7 @@ export function view(ctrl: StudyChapterNewFormCtrl): VNode {
|
|||
: 'normal';
|
||||
const noarg = trans.noarg;
|
||||
|
||||
return modal.modal({
|
||||
return snabModal({
|
||||
class: 'chapter-new',
|
||||
onClose() {
|
||||
ctrl.close();
|
||||
|
@ -342,9 +342,22 @@ export function view(ctrl: StudyChapterNewFormCtrl): VNode {
|
|||
modeChoices.map(c => option(c[0], mode, noarg(c[1])))
|
||||
),
|
||||
]),
|
||||
modal.button(noarg('createChapter')),
|
||||
modalButton(noarg('createChapter')),
|
||||
]
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export function modalButton(name: string): VNode {
|
||||
return h(
|
||||
'div.form-actions.single',
|
||||
h(
|
||||
'button.button',
|
||||
{
|
||||
attrs: { type: 'submit' },
|
||||
},
|
||||
name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { bind, titleNameToId, onInsert } from '../util';
|
||||
import { h, VNode } from 'snabbdom';
|
||||
import { modal } from '../modal';
|
||||
import { snabModal } from 'common/modal';
|
||||
import { prop, Prop } from 'common';
|
||||
import { StudyMemberMap } from './interfaces';
|
||||
import { AnalyseSocketSend } from '../socket';
|
||||
|
@ -45,7 +45,7 @@ export function view(ctrl: ReturnType<typeof makeCtrl>): VNode {
|
|||
.spectators()
|
||||
.filter(s => !ctrl.members()[titleNameToId(s)]) // remove existing members
|
||||
.sort();
|
||||
return modal({
|
||||
return snabModal({
|
||||
class: 'study__invite',
|
||||
onClose() {
|
||||
ctrl.open(false);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { h, VNode } from 'snabbdom';
|
||||
import * as modal from '../modal';
|
||||
import { snabModal } from 'common/modal';
|
||||
import { prop, Prop } from 'common';
|
||||
import { bind, bindSubmit, emptyRedButton } from '../util';
|
||||
import { StudyData } from './interfaces';
|
||||
|
@ -114,7 +114,7 @@ export function view(ctrl: StudyFormCtrl): VNode {
|
|||
['member', ctrl.trans.noarg('members')],
|
||||
['everyone', ctrl.trans.noarg('everyone')],
|
||||
];
|
||||
return modal.modal({
|
||||
return snabModal({
|
||||
class: 'study-edit',
|
||||
onClose() {
|
||||
ctrl.open(false);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as modal from '../modal';
|
||||
import { snabModal } from 'common/modal';
|
||||
import * as xhr from 'common/xhr';
|
||||
import { bind, bindSubmit, onInsert } from '../util';
|
||||
import { h, VNode } from 'snabbdom';
|
||||
|
@ -61,7 +61,7 @@ export function view(ctrl: StudyCtrl): VNode {
|
|||
let tagify: Tagify | undefined;
|
||||
|
||||
export function formView(ctrl: TopicsCtrl, userId?: string): VNode {
|
||||
return modal.modal({
|
||||
return snabModal({
|
||||
class: 'study-topics',
|
||||
onClose() {
|
||||
ctrl.open(false);
|
||||
|
|
|
@ -61,8 +61,8 @@ function commandHelp(aliases: string, args: string, desc: string) {
|
|||
|
||||
function help() {
|
||||
lichess.loadCssPath('clinput.help');
|
||||
modal(
|
||||
$(
|
||||
modal({
|
||||
content: $(
|
||||
'<h3>Commands</h3>' +
|
||||
commandHelp('/tv /follow', ' <user>', 'Watch someone play') +
|
||||
commandHelp('/play /challenge /match', ' <user>', 'Challenge someone to play') +
|
||||
|
@ -74,6 +74,6 @@ function help() {
|
|||
commandHelp('c', '', 'Focus the chat input') +
|
||||
commandHelp('esc', '', 'Close modals like this one')
|
||||
),
|
||||
'clinput-help'
|
||||
);
|
||||
class: 'clinput-help',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,23 +1,42 @@
|
|||
export default function modal(content: Cash, cls?: string, onClose?: () => void) {
|
||||
import { h, VNode } from 'snabbdom';
|
||||
import { bind, MaybeVNodes, onInsert } from './snabbdom';
|
||||
|
||||
interface BaseModal {
|
||||
class?: string;
|
||||
onInsert?: ($wrap: Cash) => void;
|
||||
onClose?(): void;
|
||||
noClickAway?: boolean;
|
||||
}
|
||||
|
||||
interface Modal extends BaseModal {
|
||||
content: Cash;
|
||||
}
|
||||
|
||||
interface SnabModal extends BaseModal {
|
||||
content: MaybeVNodes;
|
||||
}
|
||||
|
||||
export default function modal(opts: Modal) {
|
||||
modal.close();
|
||||
const $wrap = $(
|
||||
'<div id="modal-wrap"><span class="close" role="button" aria-label="Close" data-icon="" tabindex="0"></span></div>'
|
||||
);
|
||||
const $overlay = $(`<div id="modal-overlay" class="${cls}">`).on('click', modal.close);
|
||||
const $overlay = $(`<div id="modal-overlay" class="${opts.class}">`);
|
||||
if (!opts.noClickAway) $overlay.on('click', modal.close);
|
||||
$('<a href="#"></a>').appendTo($overlay); // guard against focus escaping to window chrome
|
||||
$wrap.appendTo($overlay);
|
||||
$('<a href="#"></a>').appendTo($overlay); // guard against focus escaping to window chrome
|
||||
content.clone().removeClass('none').appendTo($wrap);
|
||||
modal.onClose = onClose;
|
||||
$wrap
|
||||
.find('.close')
|
||||
.on('click', modal.close)
|
||||
.on('keydown', (e: KeyboardEvent) => (e.code === 'Space' || e.code === 'Enter' ? modal.close() : true));
|
||||
$wrap.on('click', (e: Event) => e.stopPropagation());
|
||||
opts.content.clone().removeClass('none').appendTo($wrap);
|
||||
opts.onInsert && opts.onInsert($wrap);
|
||||
modal.onClose = opts.onClose;
|
||||
$wrap.find('.close').each(function (this: HTMLElement) {
|
||||
bindClose(this, modal.close);
|
||||
});
|
||||
$('body').addClass('overlayed').prepend($overlay);
|
||||
focusFirstChild($wrap);
|
||||
bindWrap($wrap);
|
||||
return $wrap;
|
||||
}
|
||||
|
||||
modal.close = () => {
|
||||
$('body').removeClass('overlayed');
|
||||
$('#modal-overlay').each(function (this: HTMLElement) {
|
||||
|
@ -26,14 +45,57 @@ modal.close = () => {
|
|||
});
|
||||
delete modal.onClose;
|
||||
};
|
||||
|
||||
modal.onClose = undefined as (() => void) | undefined;
|
||||
|
||||
export function snabModal(opts: SnabModal): VNode {
|
||||
const close = opts.onClose!;
|
||||
return h(
|
||||
'div#modal-overlay',
|
||||
{
|
||||
...(opts.onClose && !opts.noClickAway ? { hook: bind('click', close) } : {}),
|
||||
},
|
||||
[
|
||||
h(
|
||||
'div#modal-wrap.' + opts.class,
|
||||
{
|
||||
hook: onInsert(el => {
|
||||
bindWrap($(el));
|
||||
opts.onInsert && opts.onInsert($(el));
|
||||
}),
|
||||
},
|
||||
[
|
||||
h('span.close', {
|
||||
attrs: {
|
||||
'data-icon': '',
|
||||
role: 'button',
|
||||
'aria-label': 'Close',
|
||||
tabindex: '0',
|
||||
},
|
||||
hook: onInsert(el => bindClose(el, close)),
|
||||
}),
|
||||
h('div', opts.content),
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
const bindClose = (el: HTMLElement, close: () => void) => {
|
||||
el.addEventListener('click', close);
|
||||
el.addEventListener('keydown', e => (e.code === 'Enter' || e.code === 'Space' ? close() : true));
|
||||
};
|
||||
|
||||
const bindWrap = ($wrap: Cash) => {
|
||||
$wrap.on('click', (e: Event) => e.stopPropagation());
|
||||
focusFirstChild($wrap);
|
||||
};
|
||||
|
||||
const focusableSelectors =
|
||||
'button:not(:disabled), [href], input:not(:disabled):not([type="hidden"]), select:not(:disabled), textarea:not(:disabled), [tabindex="0"]';
|
||||
|
||||
export function trapFocus(event: FocusEvent) {
|
||||
const wrap: HTMLElement | undefined = $('#modal-wrap').get(0);
|
||||
console.log(wrap);
|
||||
const wrap: HTMLElement | undefined = $('#modal-wrap')[0];
|
||||
if (!wrap) return;
|
||||
const position = wrap.compareDocumentPosition(event.target as HTMLElement);
|
||||
if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) return;
|
||||
|
@ -43,8 +105,8 @@ export function trapFocus(event: FocusEvent) {
|
|||
event.preventDefault();
|
||||
}
|
||||
|
||||
export function focusFirstChild(parent: Cash) {
|
||||
export const focusFirstChild = (parent: Cash) => {
|
||||
const children = parent.find(focusableSelectors);
|
||||
// prefer child 1 over child 0 because child 0 should be a close button
|
||||
(children.get(1) ?? children.get(0))?.focus();
|
||||
}
|
||||
(children[1] ?? children[0])?.focus();
|
||||
};
|
||||
|
|
|
@ -285,7 +285,10 @@ function controls(ctrl: EditorCtrl, state: EditorState): VNode {
|
|||
},
|
||||
on: {
|
||||
click: () => {
|
||||
if (state.playable) modal($('.continue-with'));
|
||||
if (state.playable)
|
||||
modal({
|
||||
content: $('.continue-with'),
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -90,7 +90,13 @@ export default function LichessLobby(opts: LobbyOpts) {
|
|||
res.text().then(text => {
|
||||
if (res.ok) {
|
||||
lobby.setup.prepareForm(
|
||||
modal($(text), 'game-setup', () => $startButtons.find('.active').removeClass('active'))
|
||||
modal({
|
||||
content: $(text),
|
||||
class: 'game-setup',
|
||||
onClose() {
|
||||
$startButtons.find('.active').removeClass('active');
|
||||
},
|
||||
})
|
||||
);
|
||||
lichess.contentLoaded();
|
||||
} else {
|
||||
|
|
|
@ -153,12 +153,12 @@ export function make(send: SocketSend, ctrl: RoundController): RoundSocket {
|
|||
},
|
||||
simulEnd(simul: game.Simul) {
|
||||
lichess.loadCssPath('modal');
|
||||
modal(
|
||||
$(
|
||||
modal({
|
||||
content: $(
|
||||
'<p>Simul complete!</p><br /><br />' +
|
||||
`<a class="button" href="/simul/${simul.id}">Back to ${simul.name} simul</a>`
|
||||
)
|
||||
);
|
||||
),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -49,10 +49,14 @@ export default function (showText: (ctrl: SimulCtrl) => VNode) {
|
|||
: util.bind('click', () => {
|
||||
if (ctrl.data.variants.length === 1) xhr.join(ctrl.data.id, ctrl.data.variants[0].key);
|
||||
else {
|
||||
modal($('.simul .continue-with'));
|
||||
$('#modal-wrap .continue-with a').on('click', function (this: HTMLElement) {
|
||||
modal.close();
|
||||
xhr.join(ctrl.data.id, $(this).data('variant'));
|
||||
modal({
|
||||
content: $('.simul .continue-with'),
|
||||
onInsert($wrap) {
|
||||
$wrap.find('button').on('click', function (this: HTMLElement) {
|
||||
modal.close();
|
||||
xhr.join(ctrl.data.id, $(this).data('variant'));
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
@ -200,7 +204,7 @@ export default function (showText: (ctrl: SimulCtrl) => VNode) {
|
|||
'div.continue-with.none',
|
||||
ctrl.data.variants.map(function (variant) {
|
||||
return h(
|
||||
'a.button',
|
||||
'button.button',
|
||||
{
|
||||
attrs: {
|
||||
'data-variant': variant.key,
|
||||
|
|
|
@ -135,7 +135,7 @@ export default function (publicKey: string, pricing: Pricing) {
|
|||
.redirectToCheckout({
|
||||
sessionId: data.session.id,
|
||||
})
|
||||
.then(result => showErrorThenReload(result.error.message));
|
||||
.then((result: any) => showErrorThenReload(result.error.message));
|
||||
} else location.assign('/patron');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,16 +5,20 @@ lichess.load.then(() => {
|
|||
$('.forum')
|
||||
.on('click', 'a.delete', function (this: HTMLAnchorElement) {
|
||||
const link = this;
|
||||
const $wrap = modal($('.forum-delete-modal'));
|
||||
$wrap
|
||||
.find('form')
|
||||
.attr('action', link.href)
|
||||
.on('submit', function (this: HTMLFormElement, e: Event) {
|
||||
e.preventDefault();
|
||||
xhr.formToXhr(this);
|
||||
modal.close();
|
||||
$(link).closest('.forum-post').hide();
|
||||
});
|
||||
modal({
|
||||
content: $('.forum-delete-modal'),
|
||||
onInsert($wrap) {
|
||||
$wrap
|
||||
.find('form')
|
||||
.attr('action', link.href)
|
||||
.on('submit', function (this: HTMLFormElement, e: Event) {
|
||||
e.preventDefault();
|
||||
xhr.formToXhr(this);
|
||||
modal.close();
|
||||
$(link).closest('.forum-post').hide();
|
||||
});
|
||||
},
|
||||
});
|
||||
return false;
|
||||
})
|
||||
.on('click', 'form.unsub button', function (this: HTMLButtonElement) {
|
||||
|
|
|
@ -40,23 +40,27 @@ lichess.load.then(() => {
|
|||
|
||||
$('#communication').on('click', '.line:not(.lichess)', function (this: HTMLDivElement) {
|
||||
const $l = $(this);
|
||||
const roomId = $l.parents('.game').data('room');
|
||||
const chan = $l.parents('.game').data('chan');
|
||||
const $wrap = modal($('.timeout-modal'));
|
||||
$wrap.find('.username').text($l.find('.user-link').text());
|
||||
$wrap.find('.text').text($l.text().split(' ').slice(1).join(' '));
|
||||
$wrap.on('click', '.button', function (this: HTMLButtonElement) {
|
||||
modal.close();
|
||||
text('/mod/public-chat/timeout', {
|
||||
method: 'post',
|
||||
body: form({
|
||||
roomId,
|
||||
chan,
|
||||
userId: $wrap.find('.username').text().toLowerCase(),
|
||||
reason: this.value,
|
||||
text: $wrap.find('.text').text(),
|
||||
}),
|
||||
}).then(_ => setTimeout(reloadNow, 1000));
|
||||
modal({
|
||||
content: $('.timeout-modal'),
|
||||
onInsert($wrap) {
|
||||
$wrap.find('.username').text($l.find('.user-link').text());
|
||||
$wrap.find('.text').text($l.text().split(' ').slice(1).join(' '));
|
||||
$wrap.on('click', '.button', function (this: HTMLButtonElement) {
|
||||
const roomId = $l.parents('.game').data('room');
|
||||
const chan = $l.parents('.game').data('chan');
|
||||
text('/mod/public-chat/timeout', {
|
||||
method: 'post',
|
||||
body: form({
|
||||
roomId,
|
||||
chan,
|
||||
userId: $wrap.find('.username').text().toLowerCase(),
|
||||
reason: this.value,
|
||||
text: $wrap.find('.text').text(),
|
||||
}),
|
||||
}).then(_ => setTimeout(reloadNow, 1000));
|
||||
modal.close();
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -141,8 +141,8 @@ team {
|
|||
}
|
||||
|
||||
.team-battle__choice {
|
||||
a {
|
||||
display: block;
|
||||
button {
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import TournamentController from '../ctrl';
|
||||
import { bind, onInsert, playerName } from './util';
|
||||
import { bind, playerName } from './util';
|
||||
import { h, VNode } from 'snabbdom';
|
||||
import { TeamBattle, RankedTeam, MaybeVNode } from '../interfaces';
|
||||
import modal from 'common/modal';
|
||||
import modal, { snabModal } from 'common/modal';
|
||||
|
||||
export function joinWithTeamSelector(ctrl: TournamentController) {
|
||||
const onClose = () => {
|
||||
ctrl.joinWithTeamSelector = false;
|
||||
ctrl.redraw();
|
||||
};
|
||||
const tb = ctrl.data.teamBattle!;
|
||||
return h(
|
||||
'div.none',
|
||||
{
|
||||
hook: onInsert(el => modal($(el), 'team-battle__choice', onClose)),
|
||||
return snabModal({
|
||||
class: 'team-battle__choice',
|
||||
onInsert($el) {
|
||||
$el.on('click', '.team-picker__team', e => {
|
||||
ctrl.join(e.target.dataset['id']);
|
||||
modal.close();
|
||||
});
|
||||
},
|
||||
[
|
||||
onClose() {
|
||||
ctrl.joinWithTeamSelector = false;
|
||||
ctrl.redraw();
|
||||
},
|
||||
content: [
|
||||
h('div.team-picker', [
|
||||
h('h2', 'Pick your team'),
|
||||
h('br'),
|
||||
|
@ -24,9 +27,11 @@ export function joinWithTeamSelector(ctrl: TournamentController) {
|
|||
h('p', 'Which team will you represent in this battle?'),
|
||||
...tb.joinWith.map(id =>
|
||||
h(
|
||||
'a.button',
|
||||
'button.button.team-picker__team',
|
||||
{
|
||||
hook: bind('click', () => ctrl.join(id), ctrl.redraw),
|
||||
attrs: {
|
||||
'data-id': id,
|
||||
},
|
||||
},
|
||||
tb.teams[id]
|
||||
)
|
||||
|
@ -51,8 +56,8 @@ export function joinWithTeamSelector(ctrl: TournamentController) {
|
|||
),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
);
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export function teamStanding(ctrl: TournamentController, klass?: string): VNode | null {
|
||||
|
|
Loading…
Reference in New Issue