Skip to content

Commit

Permalink
more fun with pgn-viewer
Browse files Browse the repository at this point in the history
back where we started

closes #2
  • Loading branch information
schlawg committed Sep 6, 2022
1 parent 88576e8 commit 6a2a682
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"printWidth": 120,
"arrowParens": "avoid"
}
1 change: 1 addition & 0 deletions src/ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default class Ctrl {
path: Path;
translate: Translate;
ground?: CgApi;
div?: HTMLElement;
flipped = false;
pane = 'board';
autoScrollRequested = false;
Expand Down
94 changes: 94 additions & 0 deletions src/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import Ctrl from './ctrl';

const lpvs = new Set<Ctrl>();
let viewTarget: Ctrl | undefined;

initEventHandlers();

export function stepwiseScroll(inner: (e: WheelEvent, scroll: boolean) => void): (e: WheelEvent) => void {
let scrollTotal = 0;
return (e: WheelEvent) => {
scrollTotal += e.deltaY * (e.deltaMode ? 40 : 1);
if (Math.abs(scrollTotal) >= 4) {
inner(e, true);
scrollTotal = 0;
} else {
inner(e, false);
}
};
}

export function eventRepeater(action: () => void, e: Event) {
const repeat = () => {
action();
delay = Math.max(100, delay - delay / 15);
timeout = setTimeout(repeat, delay);
};
let delay = 350;
let timeout = setTimeout(repeat, 500);
action();
const eventName = e.type == 'touchstart' ? 'touchend' : 'mouseup';
document.addEventListener(eventName, () => clearTimeout(timeout), { once: true });
}

export function addCtrl(ctrl: Ctrl) {
lpvs.add(ctrl);
adjustViewTarget(); // after all lpvs are added, we only do this again after scrolling.
}

function initEventHandlers() {
let scrollTimeout = 0;
const debouncer = () => {
if (scrollTimeout) window.clearTimeout(scrollTimeout);
scrollTimeout = window.setTimeout(adjustViewTarget, 125);
};
window.visualViewport.addEventListener('scroll', debouncer);
window.visualViewport.addEventListener('resize', debouncer);
document.addEventListener('keydown', onKeyDown);
}

function suppressKeyNavOn(e: KeyboardEvent): boolean {
const ae = document.activeElement;
if (
!viewTarget ||
e.getModifierState('Shift') ||
e.getModifierState('Alt') ||
e.getModifierState('Control') ||
e.getModifierState('Meta')
)
return true;
else if (ae instanceof HTMLInputElement)
switch ((ae as HTMLInputElement).type) {
case 'button':
case 'checkbox':
case 'color':
case 'image':
case 'radio':
case 'submit':
case 'file':
return false;
default:
return true;
}
else return ae instanceof HTMLTextAreaElement;
}

function onKeyDown(e: KeyboardEvent) {
if (suppressKeyNavOn(e)) return;
else if (e.key == 'ArrowLeft') viewTarget?.goTo('prev');
else if (e.key == 'ArrowRight') viewTarget?.goTo('next');
else if (e.key == 'f' && !(document.activeElement instanceof HTMLSelectElement)) viewTarget?.flip();
}

function adjustViewTarget() {
let largestOnscreenLpvHeight = 0;
viewTarget = undefined;
lpvs.forEach((v: Ctrl) => {
const r = v.div!.getBoundingClientRect();
const onscreenLpvHeight = Math.min(window.innerHeight, r.bottom) - Math.max(0, r.top);
if (onscreenLpvHeight > largestOnscreenLpvHeight) {
largestOnscreenLpvHeight = onscreenLpvHeight;
viewTarget = v;
}
});
}
2 changes: 1 addition & 1 deletion src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class Game {
this.mainline = Array.from(this.moves.mainline());
}

nodeAt = (path: Path): AnyNode | undefined => nodeAtPathFrom(this.moves, path)
nodeAt = (path: Path): AnyNode | undefined => nodeAtPathFrom(this.moves, path);

dataAt = (path: Path): MoveData | Initial | undefined => {
const node = this.nodeAt(path);
Expand Down
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Ctrl from './ctrl';
import view from './view/main';
import { init, attributesModule, classModule } from 'snabbdom';
import { Opts } from './interfaces';
import { Opts, GoTo } from './interfaces';
import config from './config';
import { addCtrl } from './events';

export default function start(element: HTMLElement, cfg: Partial<Opts>) {
const patch = init([classModule, attributesModule]);
Expand All @@ -14,6 +15,8 @@ export default function start(element: HTMLElement, cfg: Partial<Opts>) {
const blueprint = view(ctrl);
element.innerHTML = '';
let vnode = patch(element, blueprint);
ctrl.div = vnode.elm as HTMLElement;
addCtrl(ctrl);

function redraw() {
vnode = patch(vnode, view(ctrl));
Expand Down
4 changes: 3 additions & 1 deletion src/pgn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ test('longer mainline', () => {
test('initial position', () => {
expect(makeGame('').initial.pos.fullmoves).toBe(1);
expect(makeGame('1. e4 c5 2. Nf3').initial.pos.fullmoves).toBe(1);
expect(makeGame('[FEN "rnbqkbnr/pppp1ppp/8/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR b KQkq - 1 2"]').initial.pos.fullmoves).toBe(2);
expect(makeGame('[FEN "rnbqkbnr/pppp1ppp/8/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR b KQkq - 1 2"]').initial.pos.fullmoves).toBe(
2
);
});
3 changes: 2 additions & 1 deletion src/view/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import Ctrl from '../ctrl';
import { Chessground } from 'chessground';
import { Config as CgConfig } from 'chessground/config';
import { h, VNode } from 'snabbdom';
import { bindNonPassive, onInsert, stepwiseScroll } from './util';
import { bindNonPassive, onInsert } from './util';
import { stepwiseScroll } from '../events';
import { renderMenu, renderControls } from './menu';
import { renderMoves } from './side';
import renderPlayer from './player';
Expand Down
3 changes: 2 additions & 1 deletion src/view/menu.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { h } from 'snabbdom';
import Ctrl from '../ctrl';
import { GoTo } from '../interfaces';
import { bind, bindMobileMousedown, eventRepeater, onInsert } from './util';
import { bind, bindMobileMousedown, onInsert } from './util';
import { eventRepeater } from '../events';

export const renderMenu = (ctrl: Ctrl) =>
h('div.lpv__menu.lpv__pane', [
Expand Down
26 changes: 0 additions & 26 deletions src/view/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,3 @@ export function onInsert<A extends HTMLElement>(f: (element: A) => void): Hooks
insert: vnode => f(vnode.elm as A),
};
}

export function stepwiseScroll(inner: (e: WheelEvent, scroll: boolean) => void): (e: WheelEvent) => void {
let scrollTotal = 0;
return (e: WheelEvent) => {
scrollTotal += e.deltaY * (e.deltaMode ? 40 : 1);
if (Math.abs(scrollTotal) >= 4) {
inner(e, true);
scrollTotal = 0;
} else {
inner(e, false);
}
};
}

export function eventRepeater(action: () => void, e: Event) {
const repeat = () => {
action();
delay = Math.max(100, delay - delay / 15);
timeout = setTimeout(repeat, delay);
};
let delay = 350;
let timeout = setTimeout(repeat, 500);
action();
const eventName = e.type == 'touchstart' ? 'touchend' : 'mouseup';
document.addEventListener(eventName, () => clearTimeout(timeout), { once: true });
}

0 comments on commit 6a2a682

Please sign in to comment.