ui: Move more stuff to common

pull/9617/head^2
Benedikt Werner 2021-08-21 14:27:23 +02:00
parent e8847746dc
commit a152998418
No known key found for this signature in database
GPG Key ID: 1DBFF0F8E9E121EB
77 changed files with 162 additions and 330 deletions

View File

@ -4,8 +4,7 @@ import AnalyseCtrl from './ctrl';
import { findTag } from './study/studyChapters';
import * as game from 'game';
import { defined } from 'common';
import { bind } from 'common/snabbdom';
import { dataIcon } from './util';
import { bind, dataIcon } from 'common/snabbdom';
type AdviceKind = 'inaccuracy' | 'mistake' | 'blunder';

View File

@ -1,13 +1,12 @@
import { isEmpty } from 'common';
import modal from 'common/modal';
import { bind, bindNonPassive } from 'common/snabbdom';
import { bind, bindNonPassive, dataIcon } from 'common/snabbdom';
import { h, VNode, Hooks } from 'snabbdom';
import { MaybeVNodes } from './interfaces';
import { AutoplayDelay } from './autoplay';
import { boolSetting, BoolSetting } from './boolSetting';
import AnalyseCtrl from './ctrl';
import { cont as contRoute } from 'game/router';
import { dataIcon } from './util';
import * as pgnExport from './pgnExport';
interface AutoplaySpeed {

View File

@ -1,8 +1,7 @@
import { h, VNode } from 'snabbdom';
import { prop } from 'common';
import { bind } from 'common/snabbdom';
import { bind, dataIcon } from 'common/snabbdom';
import { storedProp, storedJsonProp, StoredJsonProp } from 'common/storage';
import { dataIcon } from '../util';
import { Game } from '../interfaces';
import { ExplorerDb, ExplorerSpeed, ExplorerConfigData, ExplorerConfigCtrl } from './interfaces';

View File

@ -1,9 +1,8 @@
import { h, VNode } from 'snabbdom';
import { numberFormat } from 'common/number';
import { bind } from 'common/snabbdom';
import { bind, dataIcon } from 'common/snabbdom';
import { defined } from 'common';
import { view as renderConfig } from './explorerConfig';
import { dataIcon } from '../util';
import { winnerOf } from './explorerUtil';
import { MaybeVNode } from '../interfaces';
import AnalyseCtrl from '../ctrl';

View File

@ -1,9 +1,9 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { bind, dataIcon } from 'common/snabbdom';
import { ForecastCtrl, ForecastStep } from './interfaces';
import AnalyseCtrl from '../ctrl';
import { renderNodesHtml } from '../pgnExport';
import { dataIcon, spinner } from '../util';
import spinner from 'common/spinner';
import { fixCrazySan } from 'chess';
function onMyTurn(ctrl: AnalyseCtrl, fctrl: ForecastCtrl, cNodes: ForecastStep[]): VNode | undefined {

View File

@ -3,7 +3,7 @@ import * as xhr from 'common/xhr';
import AnalyseCtrl from './ctrl';
import { h, VNode } from 'snabbdom';
import { snabModal } from 'common/modal';
import { spinner } from './util';
import spinner from 'common/spinner';
export const bind = (ctrl: AnalyseCtrl) => {
const kbd = window.Mousetrap;

View File

@ -1,8 +1,8 @@
import { renderIndexAndMove } from '../moveView';
import { RetroCtrl } from './retroCtrl';
import AnalyseCtrl from '../ctrl';
import { bind } from 'common/snabbdom';
import { dataIcon, spinner } from '../util';
import { bind, dataIcon } from 'common/snabbdom';
import spinner from 'common/spinner';
import { h, VNode } from 'snabbdom';
function skipOrViewSolution(ctrl: RetroCtrl) {

View File

@ -1,6 +1,7 @@
import * as chapterForm from './chapterNewForm';
import { bind, bindSubmit, onInsert } from 'common/snabbdom';
import { spinner, option, emptyRedButton } from '../util';
import spinner from 'common/spinner';
import { option, emptyRedButton } from '../util';
import { ChapterMode, EditChapterData, Orientation, StudyChapterConfig, StudyChapterMeta } from './interfaces';
import { defined, prop, Prop } from 'common';
import { h, VNode } from 'snabbdom';

View File

@ -8,7 +8,8 @@ import { h, VNode } from 'snabbdom';
import AnalyseCtrl from '../ctrl';
import { Redraw } from '../interfaces';
import { StudySocketSend } from '../socket';
import { option, spinner } from '../util';
import spinner from 'common/spinner';
import { option } from '../util';
import { ChapterData, ChapterMode, Orientation, StudyChapterMeta } from './interfaces';
import { chapter as chapterTour } from './studyTour';
import { importPgn, variants as xhrVariants } from './studyXhr';

View File

@ -1,6 +1,5 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { dataIcon } from '../../util';
import { bind, dataIcon } from 'common/snabbdom';
import AnalyseCtrl from '../../ctrl';
import { StudyCtrl } from '../interfaces';

View File

@ -1,7 +1,7 @@
import { h, VNode } from 'snabbdom';
import GamebookPlayCtrl, { Feedback } from './gamebookPlayCtrl';
import { bind } from 'common/snabbdom';
import { dataIcon, iconTag, richHTML } from '../../util';
import { bind, dataIcon } from 'common/snabbdom';
import { iconTag, richHTML } from '../../util';
// eslint-disable-next-line no-duplicate-imports
import { State } from './gamebookPlayCtrl';

View File

@ -1,7 +1,7 @@
import * as domData from 'common/data';
import debounce from 'common/debounce';
import { bind } from 'common/snabbdom';
import { spinner } from '../util';
import spinner from 'common/spinner';
import { h, VNode } from 'snabbdom';
import { MaybeVNodes } from '../interfaces';
import { multiBoard as xhrLoad } from './studyXhr';

View File

@ -1,8 +1,9 @@
import { bind, bindNonPassive } from 'common/snabbdom';
import spinner from 'common/spinner';
import { h, thunk, VNode } from 'snabbdom';
import { boolSetting } from '../../boolSetting';
import { MaybeVNodes } from '../../interfaces';
import { option, plural, richHTML, spinner } from '../../util';
import { option, plural, richHTML } from '../../util';
import { view as descView } from '../description';
import { StudyCtrl } from '../interfaces';
import { StudyPracticeCtrl, StudyPracticeData } from './interfaces';

View File

@ -1,6 +1,5 @@
import { bind, onInsert } from 'common/snabbdom';
import { bind, onInsert, dataIcon } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import { dataIcon } from '../../util';
import { LogEvent } from './interfaces';
import RelayCtrl from './relayCtrl';

View File

@ -1,6 +1,7 @@
import AnalyseCtrl from '../../ctrl';
import RelayCtrl from './relayCtrl';
import { dataIcon, innerHTML } from '../../util';
import { dataIcon } from 'common/snabbdom';
import { innerHTML } from '../../util';
import { h, VNode } from 'snabbdom';
import { RelayRound } from './interfaces';
import { StudyCtrl } from '../interfaces';

View File

@ -1,9 +1,9 @@
import { defined, Prop, prop } from 'common';
import { bind, onInsert } from 'common/snabbdom';
import spinner from 'common/spinner';
import Highcharts from 'highcharts';
import { h, VNode } from 'snabbdom';
import AnalyseCtrl from '../ctrl';
import { spinner } from '../util';
export interface ServerEvalCtrl {
requested: Prop<boolean>;

View File

@ -1,9 +1,9 @@
import { prop, Prop } from 'common';
import { bind } from 'common/snabbdom';
import { bind, dataIcon } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import AnalyseCtrl from '../ctrl';
import { StudySocketSend } from '../socket';
import { dataIcon, iconTag, scrollTo } from '../util';
import { iconTag, scrollTo } from '../util';
import { ctrl as chapterEditForm, StudyChapterEditFormCtrl } from './chapterEditForm';
import { ctrl as chapterNewForm, StudyChapterNewFormCtrl } from './chapterNewForm';
import { LocalPaths, StudyChapter, StudyChapterConfig, StudyChapterMeta, StudyCtrl, TagArray } from './interfaces';

View File

@ -1,9 +1,9 @@
import { prop, Prop } from 'common';
import { bind } from 'common/snabbdom';
import throttle from 'common/throttle';
import spinner from 'common/spinner';
import { h, VNode } from 'snabbdom';
import AnalyseCtrl from '../ctrl';
import { spinner } from '../util';
import * as xhr from './studyXhr';
interface AllGlyphs {

View File

@ -1,8 +1,8 @@
import { prop, Prop } from 'common';
import { bind, onInsert } from 'common/snabbdom';
import { bind, onInsert, dataIcon } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import { AnalyseSocketSend } from '../socket';
import { dataIcon, iconTag, scrollTo, titleNameToId } from '../util';
import { iconTag, scrollTo, titleNameToId } from '../util';
import { StudyCtrl, StudyMember, StudyMemberMap, Tab } from './interfaces';
import { makeCtrl as inviteFormCtrl, StudyInviteFormCtrl } from './inviteForm';
import { NotifCtrl } from './notif';

View File

@ -1,6 +1,6 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { dataIcon, iconTag, richHTML } from '../util';
import { bind , dataIcon} from 'common/snabbdom';
import { iconTag, richHTML } from '../util';
import { view as memberView } from './studyMembers';
import { view as chapterView } from './studyChapters';
import { view as chapterNewFormView } from './chapterNewForm';

View File

@ -1,5 +1,6 @@
import { h, VNode, Attrs } from 'snabbdom';
import { h } from 'snabbdom';
import { fixCrazySan } from 'chess';
import { dataIcon } from 'common/snabbdom';
export { autolink, innerHTML, enrichText, richHTML, toYouTubeEmbed, toTwitchEmbed } from 'common/richText';
@ -11,16 +12,6 @@ export function plyColor(ply: number): Color {
return ply % 2 === 0 ? 'white' : 'black';
}
export function bindMobileMousedown(el: HTMLElement, f: (e: Event) => unknown, redraw?: () => void) {
for (const mousedownEvent of ['touchstart', 'mousedown']) {
el.addEventListener(mousedownEvent, e => {
f(e);
e.preventDefault();
if (redraw) redraw();
});
}
}
export function bindMobileTapHold(el: HTMLElement, f: (e: Event) => unknown, redraw?: () => void) {
let longPressCountdown: number;
@ -50,12 +41,6 @@ export function readOnlyProp<A>(value: A): () => A {
};
}
export function dataIcon(icon: string): Attrs {
return {
'data-icon': icon,
};
}
export function iconTag(icon: string) {
return h('i', { attrs: dataIcon(icon) });
}
@ -78,16 +63,6 @@ export function titleNameToId(titleName: string): string {
return (split.length === 1 ? split[0] : split[1]).toLowerCase();
}
export function spinner(): VNode {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}
export function baseUrl() {
return `${window.location.protocol}//${window.location.host}`;
}

View File

@ -3,7 +3,7 @@ import { read as readFen } from 'chessground/fen';
import { parseFen } from 'chessops/fen';
import { defined } from 'common';
import changeColorHandle from 'common/coordsColor';
import { bind, bindNonPassive, MaybeVNodes, onInsert } from 'common/snabbdom';
import { bind, bindNonPassive, bindMobileMousedown, MaybeVNodes, onInsert, dataIcon } from 'common/snabbdom';
import { getPlayer, playable } from 'game';
import * as router from 'game/router';
import * as materialView from 'game/view/material';
@ -37,7 +37,7 @@ import relayTour from './study/relay/relayTourView';
import { findTag } from './study/studyChapters';
import * as studyView from './study/studyView';
import { render as renderTreeView } from './treeView/treeView';
import { bindMobileMousedown, dataIcon, spinner } from './util';
import spinner from 'common/spinner';
function renderResult(ctrl: AnalyseCtrl): VNode[] {
const render = (result: string, status: MaybeVNodes) => [h('div.result', result), h('div.status', status)];

View File

@ -1,5 +1,6 @@
import { Ctrl, Challenge, ChallengeData, ChallengeDirection, ChallengeUser, TimeControl } from './interfaces';
import { h, VNode } from 'snabbdom';
import spinner from 'common/spinner';
export function loaded(ctrl: Ctrl): VNode {
return ctrl.redirecting()
@ -201,13 +202,3 @@ function onClick(f: (e: Event) => void) {
},
};
}
function spinner() {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}

View File

@ -1,7 +1,8 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { ModerationCtrl, ModerationOpts, ModerationData, ModerationReason } from './interfaces';
import { numberFormat } from 'common/number';
import { userLink, bind } from './util';
import { userLink } from './util';
import { userModInfo } from './xhr';
export function moderationCtrl(opts: ModerationOpts): ModerationCtrl {

View File

@ -1,5 +1,5 @@
import { h, VNode } from 'snabbdom';
import { bind } from './util';
import { bind } from 'common/snabbdom';
import { Redraw } from './interfaces';
export interface PresetCtrl {

View File

@ -24,11 +24,3 @@ export function userLink(u: string, title?: string, patron?: boolean): VNode {
title && title != 'BOT' ? [line, h('span.utitle', title), trunc] : [line, trunc]
);
}
export function bind(eventName: string, f: (e: Event) => void) {
return {
insert(vnode: VNode) {
(vnode.elm as HTMLElement).addEventListener(eventName, f);
},
};
}

View File

@ -1,9 +1,9 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { Ctrl, Tab } from './interfaces';
import discussionView from './discussion';
import { noteView } from './note';
import { moderationView } from './moderation';
import { bind } from './util';
export default function (ctrl: Ctrl): VNode {
const mod = ctrl.moderation();

View File

@ -1,4 +1,4 @@
import type { VNode, Hooks } from 'snabbdom';
import type { VNode, Hooks, Attrs } from 'snabbdom';
export type MaybeVNode = VNode | string | null | undefined;
export type MaybeVNodes = MaybeVNode[];
@ -30,3 +30,23 @@ export const bindNonPassive = (eventName: string, f: (e: Event) => any, redraw?:
export function bindSubmit(f: (e: Event) => unknown, redraw?: () => void): Hooks {
return bind('submit', e => (e.preventDefault(), f(e)), redraw, false);
}
export function bindMobileMousedown(el: HTMLElement, f: (e: Event) => unknown, redraw?: () => void): void {
for (const mousedownEvent of ['touchstart', 'mousedown']) {
el.addEventListener(
mousedownEvent,
e => {
f(e);
e.preventDefault();
if (redraw) redraw();
},
{ passive: false }
);
}
}
export function dataIcon(icon: string): Attrs {
return {
'data-icon': icon,
};
}

View File

@ -0,0 +1,17 @@
import { h, VNode } from 'snabbdom';
export default function (): VNode {
return h(
'div.spinner',
{
'aria-label': 'loading',
},
[
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]
);
}

View File

@ -46,13 +46,3 @@ export function header(name: string, close: Close) {
name
);
}
export function spinner() {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}

View File

@ -1,4 +1,5 @@
import { h, VNode } from 'snabbdom';
import spinner from 'common/spinner';
import { DasherCtrl } from './dasher';
import links from './links';
@ -8,7 +9,6 @@ import { view as backgroundView } from './background';
import { view as boardView } from './board';
import { view as themeView } from './theme';
import { view as pieceView } from './piece';
import { spinner } from './util';
export function loading(): VNode {
return h('div#dasher_app.dropdown', h('div.initiating', spinner()));

View File

@ -1,5 +1,6 @@
import { h, VNode } from 'snabbdom';
import { tds, bind, perfNames } from './util';
import { bind } from 'common/snabbdom';
import { tds, perfNames } from './util';
import LobbyController from '../ctrl';
import { Seek, MaybeVNodes } from '../interfaces';
import perfIcons from 'common/perfIcons';

View File

@ -1,10 +1,10 @@
import { h, VNodeData } from 'snabbdom';
import spinner from 'common/spinner';
import renderTabs from './tabs';
import * as renderPools from './pools';
import renderRealTime from './realTime/main';
import renderSeeks from './correspondence';
import renderPlaying from './playing';
import { spinner } from './util';
import LobbyController from '../ctrl';
export default function (ctrl: LobbyController) {

View File

@ -1,6 +1,7 @@
import { h, Hooks } from 'snabbdom';
import spinner from 'common/spinner';
import { bind } from 'common/snabbdom';
import LobbyController from '../ctrl';
import { bind, spinner } from './util';
function renderRange(range: string) {
return h('div.range', range.replace('-', ''));

View File

@ -1,5 +1,5 @@
import LobbyController from '../../ctrl';
import { bind } from '../util';
import { bind } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import { Hook } from '../../interfaces';
import perfIcons from 'common/perfIcons';

View File

@ -1,6 +1,6 @@
import { h } from 'snabbdom';
import * as xhr from 'common/xhr';
import { bind } from '../util';
import { bind } from 'common/snabbdom';
import LobbyController from '../../ctrl';
function initialize(ctrl: LobbyController, el: HTMLElement) {

View File

@ -1,6 +1,7 @@
import { h } from 'snabbdom';
import LobbyController from '../../ctrl';
import { bind, tds, perfNames } from '../util';
import { bind } from 'common/snabbdom';
import { tds, perfNames } from '../util';
import perfIcons from 'common/perfIcons';
import * as hookRepo from '../../hookRepo';
import { Hook } from '../../interfaces';

View File

@ -1,5 +1,5 @@
import { h } from 'snabbdom';
import { bind } from './util';
import { bind } from 'common/snabbdom';
import LobbyController from '../ctrl';
import { MaybeVNodes, Tab } from '../interfaces';

View File

@ -1,34 +1,12 @@
import { h, Hooks } from 'snabbdom';
import { h } from 'snabbdom';
import { MaybeVNodes } from '../interfaces';
export function bind(eventName: string, f: (e: Event) => any, redraw?: () => void): Hooks {
return {
insert(vnode) {
(vnode.elm as HTMLElement).addEventListener(eventName, e => {
const res = f(e);
if (redraw) redraw();
return res;
});
},
};
}
export function tds(bits: MaybeVNodes): MaybeVNodes {
return bits.map(function (bit) {
return h('td', [bit]);
});
}
export function spinner() {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}
export const perfNames = {
ultraBullet: 'UltraBullet',
bullet: 'Bullet',

View File

@ -1,6 +1,6 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { Convo } from '../interfaces';
import { bind } from './util';
import MsgCtrl from '../ctrl';
export default function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
@ -25,6 +25,7 @@ export default function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
attrs: {
'data-icon': '',
title: ctrl.trans.noarg('blocked'),
type: 'button',
'data-hover-text': ctrl.trans.noarg('unblock'),
},
hook: bind('click', ctrl.unblock),
@ -36,6 +37,7 @@ export default function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
key: 'block',
attrs: {
'data-icon': '',
type: 'button',
title: ctrl.trans.noarg('block'),
},
hook: bind('click', withConfirm(ctrl.block)),
@ -46,6 +48,7 @@ export default function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
key: 'delete',
attrs: {
'data-icon': '',
type: 'button',
title: ctrl.trans.noarg('delete'),
},
hook: bind('click', withConfirm(ctrl.delete)),
@ -57,6 +60,7 @@ export default function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
key: 'report',
attrs: {
'data-icon': '',
type: 'button',
title: ctrl.trans('reportXToModerators', convo.user.name),
},
hook: bind('click', withConfirm(ctrl.report)),

View File

@ -1,7 +1,7 @@
import { h, VNode } from 'snabbdom';
import { bindSubmit } from 'common/snabbdom';
import { User } from '../interfaces';
import MsgCtrl from '../ctrl';
import { bind } from './util';
import throttle from 'common/throttle';
export default function renderInteract(ctrl: MsgCtrl, user: User): VNode {
@ -9,8 +9,7 @@ export default function renderInteract(ctrl: MsgCtrl, user: User): VNode {
return h(
'form.msg-app__convo__post',
{
hook: bind('submit', e => {
e.preventDefault();
hook: bindSubmit(e => {
const area = (e.target as HTMLElement).querySelector('textarea');
if (area) {
area.dispatchEvent(new Event('send'));

View File

@ -1,9 +1,9 @@
import { h, VNode } from 'snabbdom';
import spinner from 'common/spinner';
import MsgCtrl from '../ctrl';
import renderConvo from './convo';
import renderContact from './contact';
import * as search from './search';
import { spinner } from './util';
export default function (ctrl: MsgCtrl): VNode {
const activeId = ctrl.data.convo?.user.id;

View File

@ -1,9 +1,9 @@
import { h, VNode } from 'snabbdom';
import * as xhr from 'common/xhr';
import { bind } from 'common/snabbdom';
import { Convo, Msg, Daily } from '../interfaces';
import * as enhance from './enhance';
import * as xhr from 'common/xhr';
import { scroller } from './scroller';
import { bind } from './util';
import MsgCtrl from '../ctrl';
export default function renderMsgs(ctrl: MsgCtrl, convo: Convo): VNode {
@ -23,6 +23,9 @@ export default function renderMsgs(ctrl: MsgCtrl, convo: Convo): VNode {
'button.msg-app__convo__msgs__more.button.button-empty',
{
key: 'more',
attrs: {
type: 'button',
},
hook: bind('click', _ => {
scroller.setMarker();
ctrl.getMore();

View File

@ -1,4 +1,5 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import { User } from '../interfaces';
export function userIcon(user: User, cls: string): VNode {
@ -19,28 +20,6 @@ export const userName = (user: User): Array<string | VNode> =>
? [h('span.utitle', user.title == 'BOT' ? { attrs: { 'data-bot': true } } : {}, user.title), ' ', user.name]
: [user.name];
export function bind(eventName: string, f: (e: Event) => void) {
return {
insert(vnode: VNode) {
(vnode.elm as HTMLElement).addEventListener(eventName, e => {
e.stopPropagation();
f(e);
return false;
});
},
};
}
export function bindMobileMousedown(f: (e: Event) => any) {
return bind('ontouchstart' in window ? 'click' : 'mousedown', f);
}
export function spinner(): VNode {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}

View File

@ -1,5 +1,6 @@
import { Ctrl, NotifyData, Notification } from './interfaces';
import { h, VNode } from 'snabbdom';
import spinner from 'common/spinner';
import makeRenderers from './renderers';
export default function view(ctrl: Ctrl): VNode {
@ -102,9 +103,3 @@ function recentNotifications(d: NotifyData, scrolling: boolean): VNode {
function empty() {
return h('div.empty.text', { attrs: { 'data-icon': '' } }, 'No notifications.');
}
function spinner() {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [h('circle', { attrs: { cx: 20, cy: 20, r: 18, fill: 'none' } })]),
]);
}

View File

@ -29,7 +29,7 @@ import { renderSetting } from 'nvui/setting';
import { Notify } from 'nvui/notify';
import { commands } from 'nvui/command';
import * as control from '../control';
import { bind, onInsert } from '../util';
import { bind, onInsert } from 'common/snabbdom';
import { Api } from 'chessground/api';
import throttle from 'common/throttle';

View File

@ -1,35 +0,0 @@
import { Hooks } from 'snabbdom';
export function bindMobileMousedown(el: HTMLElement, f: (e: Event) => unknown, redraw?: () => void): void {
for (const mousedownEvent of ['touchstart', 'mousedown']) {
el.addEventListener(
mousedownEvent,
e => {
f(e);
e.preventDefault();
if (redraw) redraw();
},
{ passive: false }
);
}
}
export function bind(eventName: string, f: (e: Event) => unknown, redraw?: () => void): Hooks {
return onInsert(el =>
el.addEventListener(eventName, e => {
const res = f(e);
if (redraw) redraw();
return res;
})
);
}
export function onInsert<A extends HTMLElement>(f: (element: A) => void): Hooks {
return {
insert: vnode => f(vnode.elm as A),
};
}
export const dataIcon = (icon: string) => ({
'data-icon': icon,
});

View File

@ -1,4 +1,4 @@
import { bind, dataIcon } from '../util';
import { bind, dataIcon } from 'common/snabbdom';
import { Controller, MaybeVNodes } from '../interfaces';
import { h, VNode } from 'snabbdom';

View File

@ -1,7 +1,7 @@
import afterView from './after';
import { bind } from '../util';
import { Controller, MaybeVNode } from '../interfaces';
import { bind } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import { Controller, MaybeVNode } from '../interfaces';
import afterView from './after';
const viewSolution = (ctrl: Controller): VNode =>
ctrl.streak

View File

@ -6,7 +6,7 @@ import chessground from './chessground';
import feedbackView from './feedback';
import { Controller } from '../interfaces';
import { h, VNode } from 'snabbdom';
import { onInsert, bind, bindMobileMousedown } from '../util';
import { onInsert, bindMobileMousedown, bindNonPassive } from 'common/snabbdom';
import { render as treeView } from './tree';
import { view as cevalView } from 'ceval';
@ -112,7 +112,7 @@ export default function (ctrl: Controller): VNode {
hook:
'ontouchstart' in window || lichess.storage.get('scrollMoves') == '0'
? undefined
: bind('wheel', e => wheel(ctrl, e as WheelEvent)),
: bindNonPassive('wheel', e => wheel(ctrl, e as WheelEvent)),
},
[chessground(ctrl), ctrl.promotion.view()]
),

View File

@ -1,5 +1,5 @@
import { Controller, Puzzle, PuzzleGame, MaybeVNode, PuzzleDifficulty } from '../interfaces';
import { dataIcon, onInsert, bind } from '../util';
import { dataIcon, onInsert, bind } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import { numberFormat } from 'common/number';
import PuzzleStreak from '../streak';

View File

@ -1,4 +1,4 @@
import { bind, dataIcon } from '../util';
import { bind, dataIcon } from 'common/snabbdom';
import { Controller, MaybeVNode } from '../interfaces';
import { h, VNode } from 'snabbdom';

View File

@ -1,5 +1,5 @@
import * as cg from 'chessground/types';
import { h, VNodeData } from 'snabbdom';
import { VNodeData } from 'snabbdom';
import { Dests, EncodedDests } from './interfaces';
export { bind, onInsert } from 'common/snabbdom';
@ -24,18 +24,3 @@ export function parsePossibleMoves(dests?: EncodedDests): Dests {
else for (const k in dests) dec.set(k, dests[k].match(/.{2}/g) as cg.Key[]);
return dec;
}
export const spinner = () =>
h(
'div.spinner',
{
'aria-label': 'loading',
},
[
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]
);

View File

@ -1,4 +1,5 @@
import { h, VNode, Hooks } from 'snabbdom';
import spinner from 'common/spinner';
import * as util from '../util';
import * as game from 'game';
import * as status from 'game/status';
@ -81,7 +82,7 @@ function rematchButtons(ctrl: RoundController): MaybeVNodes {
ctrl.redraw
),
},
[me ? util.spinner() : h('span', noarg('rematch'))]
[me ? spinner() : h('span', noarg('rematch'))]
),
];
}

View File

@ -1,4 +1,5 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import SimulCtrl from '../ctrl';
import { Applicant } from '../interfaces';
import xhr from '../xhr';
@ -33,7 +34,7 @@ export default function (showText: (ctrl: SimulCtrl) => VNode) {
? h(
'a.button',
{
hook: util.bind('click', () => xhr.withdraw(ctrl.data.id)),
hook: bind('click', () => xhr.withdraw(ctrl.data.id)),
},
ctrl.trans('withdraw')
)
@ -46,7 +47,7 @@ export default function (showText: (ctrl: SimulCtrl) => VNode) {
},
hook: ctrl.teamBlock()
? {}
: util.bind('click', () => {
: bind('click', () => {
if (ctrl.data.variants.length === 1) xhr.join(ctrl.data.id, ctrl.data.variants[0].key);
else {
modal({
@ -133,7 +134,7 @@ export default function (showText: (ctrl: SimulCtrl) => VNode) {
'data-icon': '',
title: 'Accept',
},
hook: util.bind('click', () => xhr.accept(applicant.player.id)(ctrl.data.id)),
hook: bind('click', () => xhr.accept(applicant.player.id)(ctrl.data.id)),
}),
]
: []
@ -184,7 +185,7 @@ export default function (showText: (ctrl: SimulCtrl) => VNode) {
attrs: {
'data-icon': '',
},
hook: util.bind('click', () => xhr.reject(applicant.player.id)(ctrl.data.id)),
hook: bind('click', () => xhr.reject(applicant.player.id)(ctrl.data.id)),
}),
]
: []
@ -228,7 +229,7 @@ const randomButton = (ctrl: SimulCtrl) =>
attrs: {
'data-icon': '',
},
hook: util.bind('click', () => {
hook: bind('click', () => {
const candidates = ctrl.candidates();
const randomCandidate = candidates[Math.floor(Math.random() * candidates.length)];
xhr.accept(randomCandidate.player.id)(ctrl.data.id);
@ -246,7 +247,7 @@ const startOrCancel = (ctrl: SimulCtrl, accepted: Applicant[]) =>
attrs: {
'data-icon': '',
},
hook: util.bind('click', () => xhr.start(ctrl.data.id)),
hook: bind('click', () => xhr.start(ctrl.data.id)),
},
`Start (${accepted.length})`
)
@ -256,7 +257,7 @@ const startOrCancel = (ctrl: SimulCtrl, accepted: Applicant[]) =>
attrs: {
'data-icon': '',
},
hook: util.bind('click', () => {
hook: bind('click', () => {
if (confirm('Delete this simul?')) xhr.abort(ctrl.data.id);
}),
},

View File

@ -1,4 +1,5 @@
import { h } from 'snabbdom';
import { onInsert } from 'common/snabbdom';
import SimulCtrl from '../ctrl';
import * as util from './util';
import created from './created';
@ -18,7 +19,7 @@ export default function (ctrl: SimulCtrl) {
},
[
h('aside.simul__side', {
hook: util.onInsert(el => {
hook: onInsert(el => {
$(el).replaceWith(ctrl.opts.$side);
if (ctrl.opts.chat) {
ctrl.opts.chat.data.hostId = ctrl.data.host.id;
@ -38,7 +39,7 @@ export default function (ctrl: SimulCtrl) {
handler(ctrl)
),
h('div.chat__members.none', {
hook: util.onInsert(lichess.watchers),
hook: onInsert(lichess.watchers),
}),
]
);

View File

@ -1,7 +1,7 @@
import { h } from 'snabbdom';
import { onInsert } from 'common/snabbdom';
import SimulCtrl from '../ctrl';
import { Pairing } from '../interfaces';
import { onInsert } from './util';
import { opposite } from 'chessground/util';
export default function (ctrl: SimulCtrl) {

View File

@ -1,19 +1,7 @@
import { h, Hooks, VNode } from 'snabbdom';
import { h } from 'snabbdom';
import { Player } from '../interfaces';
import SimulCtrl from '../ctrl';
export function onInsert(f: (element: HTMLElement) => void): Hooks {
return {
insert(vnode: VNode) {
f(vnode.elm as HTMLElement);
},
};
}
export function bind(eventName: string, f: (e: Event) => void): Hooks {
return onInsert(el => el.addEventListener(eventName, f));
}
export function player(p: Player) {
return h(
'a.ulpt.user-link.online',

View File

@ -1,7 +1,7 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import SwissCtrl from './ctrl';
import { MaybeVNodes, Pager } from './interfaces';
import { bind } from './view/util';
import * as search from './search';
export const maxPerPage = 10;

View File

@ -1,6 +1,6 @@
import { h, VNode } from 'snabbdom';
import { bind, onInsert } from 'common/snabbdom';
import TournamentController from './ctrl';
import { bind, onInsert } from './view/util';
export function button(ctrl: TournamentController): VNode {
return h('button.fbt', {

View File

@ -1,6 +1,6 @@
import { h, VNode } from 'snabbdom';
import { dataIcon } from 'common/snabbdom';
import SwissCtrl from '../ctrl';
import { dataIcon } from './util';
function startClock(time: number) {
return {

View File

@ -1,5 +1,7 @@
import { h, VNode } from 'snabbdom';
import { spinner, dataIcon, bind, onInsert, numberRow } from './util';
import spinner from 'common/spinner';
import { dataIcon, bind, onInsert } from 'common/snabbdom';
import { numberRow } from './util';
import SwissCtrl from '../ctrl';
import * as pagination from '../pagination';
import { MaybeVNodes, SwissData, Pager } from '../interfaces';

View File

@ -1,5 +1,7 @@
import { h, VNode } from 'snabbdom';
import { spinner, bind, userName, dataIcon, player as renderPlayer, numberRow } from './util';
import spinner from 'common/spinner';
import { bind, dataIcon } from 'common/snabbdom';
import { userName, player as renderPlayer, numberRow } from './util';
import { Pairing } from '../interfaces';
import { isOutcome } from '../util';
import SwissCtrl from '../ctrl';

View File

@ -1,6 +1,7 @@
import { h, VNode } from 'snabbdom';
import { bind, onInsert } from 'common/snabbdom';
import SwissCtrl from '../ctrl';
import { player as renderPlayer, bind, onInsert } from './util';
import { player as renderPlayer } from './util';
import { MaybeVNodes, Player, Pager } from '../interfaces';
function playerTr(ctrl: SwissCtrl, player: Player) {

View File

@ -1,31 +1,7 @@
import { Attrs, h, Hooks, VNode } from 'snabbdom';
import { h } from 'snabbdom';
import { BasePlayer } from '../interfaces';
import { numberFormat } from 'common/number';
export function bind(eventName: string, f: (e: Event) => any, redraw?: () => void): Hooks {
return onInsert(el =>
el.addEventListener(eventName, e => {
const res = f(e);
if (redraw) redraw();
return res;
})
);
}
export function onInsert(f: (element: HTMLElement) => void): Hooks {
return {
insert(vnode: VNode) {
f(vnode.elm as HTMLElement);
},
};
}
export function dataIcon(icon: string): Attrs {
return {
'data-icon': icon,
};
}
export const userName = (u: LightUser) => (u.title ? [h('span.utitle', u.title), ' ' + u.name] : [u.name]);
export function player(p: BasePlayer, asLink: boolean, withRating: boolean) {
@ -61,13 +37,3 @@ export function numberRow(name: string, value: any, typ?: string) {
),
]);
}
export function spinner(): VNode {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}

View File

@ -1,7 +1,7 @@
import { h, VNode } from 'snabbdom';
import { bind } from 'common/snabbdom';
import TournamentController from './ctrl';
import { MaybeVNodes, Pagination } from './interfaces';
import { bind } from './view/util';
import * as search from './search';
export const maxPerPage = 10;

View File

@ -1,6 +1,6 @@
import { h, VNode } from 'snabbdom';
import { bind, onInsert } from 'common/snabbdom';
import TournamentController from './ctrl';
import { bind, onInsert } from './view/util';
export function button(ctrl: TournamentController): VNode {
return h('button.fbt', {

View File

@ -1,6 +1,7 @@
import { h, VNode } from 'snabbdom';
import { bind, dataIcon } from 'common/snabbdom';
import TournamentController from '../ctrl';
import { player as renderPlayer, ratio2percent, bind, dataIcon, playerName } from './util';
import { player as renderPlayer, ratio2percent, playerName } from './util';
import { teamName } from './battle';
import { MaybeVNodes, Pagination, PodiumPlayer, Score, StandingPlayer } from '../interfaces';
import * as button from './button';

View File

@ -1,5 +1,6 @@
import TournamentController from '../ctrl';
import { bind, playerName } from './util';
import { bind } from 'common/snabbdom';
import { playerName } from './util';
import { h, VNode } from 'snabbdom';
import { TeamBattle, RankedTeam, MaybeVNode } from '../interfaces';
import modal, { snabModal } from 'common/modal';

View File

@ -1,6 +1,7 @@
import { h, VNode } from 'snabbdom';
import spinner from 'common/spinner';
import { bind, dataIcon } from 'common/snabbdom';
import { isIn } from '../tournament';
import { spinner, bind, dataIcon } from './util';
import TournamentController from '../ctrl';
function orJoinSpinner(ctrl: TournamentController, f: () => VNode): VNode {

View File

@ -1,11 +1,11 @@
import { h, VNode } from 'snabbdom';
import { onInsert } from 'common/snabbdom';
import TournamentController from '../ctrl';
import { MaybeVNodes } from '../interfaces';
import * as pagination from '../pagination';
import { controls, standing } from './arena';
import { teamStanding } from './battle';
import teamInfo from './teamInfo';
import { onInsert } from './util';
import header from './header';
export const name = 'created';

View File

@ -1,6 +1,6 @@
import { h, Hooks, VNode } from 'snabbdom';
import { dataIcon } from 'common/snabbdom';
import TournamentController from '../ctrl';
import { dataIcon } from './util';
import perfIcons from 'common/perfIcons';
import { TournamentData } from '../interfaces';

View File

@ -1,8 +1,8 @@
import { h, VNode } from 'snabbdom';
import { onInsert } from 'common/snabbdom';
import * as created from './created';
import * as started from './started';
import * as finished from './finished';
import { onInsert } from './util';
import { joinWithTeamSelector } from './battle';
import TournamentController from '../ctrl';
import { MaybeVNodes } from '../interfaces';

View File

@ -1,5 +1,7 @@
import { h, VNode } from 'snabbdom';
import { spinner, bind, numberRow, playerName, dataIcon, player as renderPlayer } from './util';
import spinner from 'common/spinner';
import { bind, dataIcon } from 'common/snabbdom';
import { numberRow, playerName, player as renderPlayer } from './util';
import { teamName } from './battle';
import * as status from 'game/status';
import TournamentController from '../ctrl';

View File

@ -1,6 +1,7 @@
import { h, VNode } from 'snabbdom';
import { opposite } from 'chessground/util';
import { player as renderPlayer, bind, onInsert } from './util';
import { bind, onInsert } from 'common/snabbdom';
import { player as renderPlayer } from './util';
import { Duel, DuelPlayer, DuelTeams, TeamBattle, FeaturedGame } from '../interfaces';
import { teamName } from './battle';
import TournamentController from '../ctrl';

View File

@ -1,6 +1,8 @@
import { h, VNode } from 'snabbdom';
import spinner from 'common/spinner';
import { bind, dataIcon } from 'common/snabbdom';
import TournamentController from '../ctrl';
import { bind, numberRow, spinner, dataIcon, player as renderPlayer } from './util';
import { numberRow, player as renderPlayer } from './util';
import { teamName } from './battle';
export default function (ctrl: TournamentController): VNode | undefined {

View File

@ -1,31 +1,8 @@
import { Attrs, h, Hooks, VNode, VNodeChildren } from 'snabbdom';
import { h, VNode, VNodeChildren } from 'snabbdom';
import { numberFormat } from 'common/number';
import { dataIcon } from 'common/snabbdom';
import { SimplePlayer } from '../interfaces';
export function bind(eventName: string, f: (e: Event) => any, redraw?: () => void): Hooks {
return onInsert(el =>
el.addEventListener(eventName, e => {
const res = f(e);
if (redraw) redraw();
return res;
})
);
}
export function onInsert(f: (element: HTMLElement) => void): Hooks {
return {
insert(vnode) {
f(vnode.elm as HTMLElement);
},
};
}
export function dataIcon(icon: string): Attrs {
return {
'data-icon': icon,
};
}
export function ratio2percent(r: number) {
return Math.round(100 * r) + '%';
}
@ -72,13 +49,3 @@ export function numberRow(name: string, value: any, typ?: string) {
),
]);
}
export function spinner(): VNode {
return h('div.spinner', [
h('svg', { attrs: { viewBox: '0 0 40 40' } }, [
h('circle', {
attrs: { cx: 20, cy: 20, r: 18, fill: 'none' },
}),
]),
]);
}