Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modules/analyse/src/main/ui/AnalyseI18n.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ final class AnalyseI18n(helpers: Helpers):
site.promoteVariation,
site.makeMainLine,
site.deleteFromHere,
site.collapseVariations,
site.expandVariations,
site.forceVariation,
site.copyVariationPgn,
// practice (also uses checkmate, draw)
Expand Down
2 changes: 2 additions & 0 deletions modules/coreI18n/src/main/key.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ object I18nKey:
val `promoteVariation`: I18nKey = "promoteVariation"
val `makeMainLine`: I18nKey = "makeMainLine"
val `deleteFromHere`: I18nKey = "deleteFromHere"
val `collapseVariations`: I18nKey = "collapseVariations"
val `expandVariations`: I18nKey = "expandVariations"
val `forceVariation`: I18nKey = "forceVariation"
val `copyVariationPgn`: I18nKey = "copyVariationPgn"
val `move`: I18nKey = "move"
Expand Down
2 changes: 2 additions & 0 deletions translation/source/site.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
<string name="promoteVariation">Promote variation</string>
<string name="makeMainLine">Make mainline</string>
<string name="deleteFromHere">Delete from here</string>
<string name="collapseVariations">Collapse variations</string>
<string name="expandVariations">Expand variations</string>
<string name="forceVariation">Force variation</string>
<string name="copyVariationPgn">Copy variation PGN</string>
<string name="move">Move</string>
Expand Down
1 change: 1 addition & 0 deletions ui/@types/lichess/tree.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ declare namespace Tree {
}
export interface Node extends NodeBase {
children: Node[];
collapsed?: boolean;
}

export interface NodeCrazy {
Expand Down
16 changes: 16 additions & 0 deletions ui/analyse/src/ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ export default class AnalyseCtrl {
this.path = path;
this.nodeList = this.tree.getNodeList(path);
this.node = treeOps.last(this.nodeList) as Tree.Node;
for (let i = 0; i < this.nodeList.length; i++) {
this.nodeList[i].collapsed = false;
}
this.mainline = treeOps.mainlineNodeList(this.tree.root);
this.onMainline = this.tree.pathIsMainline(path);
this.fenInput = undefined;
Expand Down Expand Up @@ -636,6 +639,19 @@ export default class AnalyseCtrl {
this.treeVersion++;
}

setCollapsed(path: Tree.Path, collapsed: boolean): void {
this.tree.setCollapsedAt(path, collapsed);
this.redraw();
}

setAllCollapsed(path: Tree.Path, collapsed: boolean): void {
// Also update parent
const parentPath = treePath.init(path);
this.tree.setCollapsedAt(parentPath, collapsed);
this.tree.setCollapsedRecursive(path, collapsed);
this.redraw();
}

forceVariation(path: Tree.Path, force: boolean): void {
this.tree.forceVariationAt(path, force);
this.jump(path);
Expand Down
77 changes: 54 additions & 23 deletions ui/analyse/src/treeView/columnView.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { VNode } from 'snabbdom';
import { isEmpty } from 'common';
import * as licon from 'common/licon';
import { LooseVNodes, looseH as h } from 'common/snabbdom';
import { fixCrazySan } from 'chess';
import { path as treePath, ops as treeOps } from 'tree';
Expand Down Expand Up @@ -48,13 +49,28 @@ function renderChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): LooseVNodes |
if (!cs[1] && isEmpty(commentTags) && !main.forceVariation)
return [
isWhite && moveView.renderIndex(main.ply, false),
...renderMoveAndChildrenOf(ctx, main, { parentPath: opts.parentPath, isMainline: true, conceal }),
...renderMoveAndChildrenOf(ctx, main, {
parentPath: opts.parentPath,
isMainline: true,
depth: opts.depth,
conceal,
}),
];
const mainChildren =
!main.forceVariation &&
renderChildrenOf(ctx, main, { parentPath: opts.parentPath + main.id, isMainline: true, conceal });

const passOpts = { parentPath: opts.parentPath, isMainline: !main.forceVariation, conceal };
renderChildrenOf(ctx, main, {
parentPath: opts.parentPath + main.id,
isMainline: true,
depth: opts.depth,
conceal,
});

const passOpts = {
parentPath: opts.parentPath,
isMainline: !main.forceVariation,
depth: opts.depth,
conceal,
};

return [
isWhite && moveView.renderIndex(main.ply, false),
Expand All @@ -63,9 +79,10 @@ function renderChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): LooseVNodes |
h(
'interrupt',
commentTags.concat(
renderLines(ctx, main.forceVariation ? cs : cs.slice(1), {
renderLines(ctx, node, main.forceVariation ? cs : cs.slice(1), {
parentPath: opts.parentPath,
isMainline: passOpts.isMainline,
depth: opts.depth,
conceal,
noConceal: !conceal,
}),
Expand All @@ -77,7 +94,7 @@ function renderChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): LooseVNodes |
];
}
if (!cs[1]) return renderMoveAndChildrenOf(ctx, main, opts);
return renderInlined(ctx, cs, opts) || [renderLines(ctx, cs, opts)];
return renderInlined(ctx, cs, opts) || [renderLines(ctx, node, cs, opts)];
}

function renderInlined(ctx: Ctx, nodes: Tree.Node[], opts: Opts): LooseVNodes | undefined {
Expand All @@ -88,30 +105,42 @@ function renderInlined(ctx: Ctx, nodes: Tree.Node[], opts: Opts): LooseVNodes |
return renderMoveAndChildrenOf(ctx, nodes[0], {
parentPath: opts.parentPath,
isMainline: false,
depth: opts.depth,
noConceal: opts.noConceal,
inline: nodes[1],
});
}

function renderLines(ctx: Ctx, nodes: Tree.Node[], opts: Opts): VNode {
function renderLines(ctx: Ctx, parentNode: Tree.Node, nodes: Tree.Node[], opts: Opts): VNode {
const collapsed =
parentNode.collapsed === undefined ? opts.depth >= 2 && opts.depth % 2 === 0 : parentNode.collapsed;
return h(
'lines',
{ class: { single: !nodes[1] } },
nodes.map(n => {
return (
retroLine(ctx, n) ||
h(
'line',
renderMoveAndChildrenOf(ctx, n, {
parentPath: opts.parentPath,
isMainline: false,
withIndex: true,
noConceal: opts.noConceal,
truncate: n.comp && !treePath.contains(ctx.ctrl.path, opts.parentPath + n.id) ? 3 : undefined,
{ class: { single: !nodes[1], collapsed } },
collapsed
? h('line', { class: { expand: true } }, [
h('branch'),
h('a', {
attrs: { 'data-icon': licon.PlusButton, title: ctx.ctrl.trans.noarg('expandVariations') },
on: { click: () => ctx.ctrl.setCollapsed(opts.parentPath, false) },
}),
)
);
}),
])
: nodes.map(n => {
return (
retroLine(ctx, n) ||
h('line', [
h('branch'),
...renderMoveAndChildrenOf(ctx, n, {
parentPath: opts.parentPath,
isMainline: false,
depth: opts.depth + 1,
withIndex: true,
noConceal: opts.noConceal,
truncate: n.comp && !treePath.contains(ctx.ctrl.path, opts.parentPath + n.id) ? 3 : undefined,
}),
])
);
}),
);
}

Expand Down Expand Up @@ -146,6 +175,7 @@ function renderMoveAndChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): LooseVN
...(renderChildrenOf(ctx, node, {
parentPath: path,
isMainline: opts.isMainline,
depth: opts.depth,
noConceal: opts.noConceal,
truncate: opts.truncate ? opts.truncate - 1 : undefined,
}) || []),
Expand All @@ -159,6 +189,7 @@ function renderInline(ctx: Ctx, node: Tree.Node, opts: Opts): VNode {
withIndex: true,
parentPath: opts.parentPath,
isMainline: false,
depth: opts.depth,
noConceal: opts.noConceal,
truncate: opts.truncate,
}),
Expand Down Expand Up @@ -201,6 +232,6 @@ export default function (ctrl: AnalyseCtrl, concealOf?: ConcealOf): VNode {
!isEmpty(commentTags) && h('interrupt', commentTags),
blackStarts && moveView.renderIndex(root.ply, false),
blackStarts && emptyMove(),
...(renderChildrenOf(ctx, root, { parentPath: '', isMainline: true }) || []),
...(renderChildrenOf(ctx, root, { parentPath: '', isMainline: true, depth: 0 }) || []),
]);
}
4 changes: 3 additions & 1 deletion ui/analyse/src/treeView/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ const autoScroll = throttle(200, (ctrl: AnalyseCtrl, el: HTMLElement) => {
cont.scrollTop = ctrl.path ? 99999 : 0;
return;
}
cont.scrollTop = target.offsetTop - cont.offsetHeight / 2 + target.offsetHeight;
const targetOffset = target.getBoundingClientRect().y - el.getBoundingClientRect().y;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we now require 2 calls to getBoundingClientRect?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put a note in the "Fix autoscroll offset" commit, let me copy it here:

We've added "position: relative" to the <line> elements for styling purposes, but that affects the calculation of the "offsetTop" property. We can instead calculate the offset ourselves.

cont.scrollTop = targetOffset - cont.offsetHeight / 2 + target.offsetHeight;
});

export interface NodeClasses {
Expand Down Expand Up @@ -164,6 +165,7 @@ export interface Ctx {
export interface Opts {
parentPath: Tree.Path;
isMainline: boolean;
depth: number;
inline?: Tree.Node;
withIndex?: boolean;
truncate?: number;
Expand Down
4 changes: 4 additions & 0 deletions ui/analyse/src/treeView/contextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ function view(opts: Opts, coords: Coords): VNode {

action(licon.Trash, trans('deleteFromHere'), () => ctrl.deleteNode(opts.path)),

action(licon.PlusButton, trans('expandVariations'), () => ctrl.setAllCollapsed(opts.path, false)),

action(licon.MinusButton, trans('collapseVariations'), () => ctrl.setAllCollapsed(opts.path, true)),

...(ctrl.study ? studyView.contextMenu(ctrl.study, opts.path, node) : []),

onMainline &&
Expand Down
59 changes: 42 additions & 17 deletions ui/analyse/src/treeView/inlineView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { fixCrazySan } from 'chess';
import { path as treePath, ops as treeOps } from 'tree';
import * as moveView from '../view/moveView';
import AnalyseCtrl from '../ctrl';
import * as licon from 'common/licon';
import { MaybeVNodes } from 'common/snabbdom';
import { mainHook, nodeClasses, renderInlineCommentsOf, retroLine, Ctx, Opts, renderingCtx } from './common';

Expand All @@ -15,6 +16,7 @@ function renderChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): MaybeVNodes |
return renderMoveAndChildrenOf(ctx, main, {
parentPath: opts.parentPath,
isMainline: true,
depth: opts.depth,
withIndex: opts.withIndex,
});
return (
Expand All @@ -25,29 +27,32 @@ function renderChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): MaybeVNodes |
renderMoveOf(ctx, main, {
parentPath: opts.parentPath,
isMainline: true,
depth: opts.depth,
withIndex: opts.withIndex,
}),
...renderInlineCommentsOf(ctx, main, opts.parentPath),
]),
h(
'interrupt',
renderLines(ctx, main.forceVariation ? cs : cs.slice(1), {
renderLines(ctx, node, main.forceVariation ? cs : cs.slice(1), {
parentPath: opts.parentPath,
isMainline: true,
depth: opts.depth,
}),
),
...(main.forceVariation
? []
: renderChildrenOf(ctx, main, {
parentPath: opts.parentPath + main.id,
isMainline: true,
depth: opts.depth,
withIndex: true,
}) || []),
]
);
}
if (!cs[1]) return renderMoveAndChildrenOf(ctx, main, opts);
return renderInlined(ctx, cs, opts) || [renderLines(ctx, cs, opts)];
return renderInlined(ctx, cs, opts) || [renderLines(ctx, node, cs, opts)];
}

function renderInlined(ctx: Ctx, nodes: Tree.Node[], opts: Opts): MaybeVNodes | undefined {
Expand All @@ -58,27 +63,40 @@ function renderInlined(ctx: Ctx, nodes: Tree.Node[], opts: Opts): MaybeVNodes |
return renderMoveAndChildrenOf(ctx, nodes[0], {
parentPath: opts.parentPath,
isMainline: opts.isMainline,
depth: opts.depth,
inline: nodes[1],
});
}

function renderLines(ctx: Ctx, nodes: Tree.Node[], opts: Opts): VNode {
function renderLines(ctx: Ctx, parentNode: Tree.Node, nodes: Tree.Node[], opts: Opts): VNode {
const collapsed =
parentNode.collapsed === undefined ? opts.depth >= 2 && opts.depth % 2 === 0 : parentNode.collapsed;
return h(
'lines',
nodes.map(n => {
return (
retroLine(ctx, n) ||
h(
'line',
renderMoveAndChildrenOf(ctx, n, {
parentPath: opts.parentPath,
isMainline: false,
withIndex: true,
truncate: n.comp && !treePath.contains(ctx.ctrl.path, opts.parentPath + n.id) ? 3 : undefined,
{ class: { collapsed } },
collapsed
? h('line', { class: { expand: true } }, [
h('branch'),
h('a', {
attrs: { 'data-icon': licon.PlusButton, title: ctx.ctrl.trans.noarg('expandVariations') },
on: { click: () => ctx.ctrl.setCollapsed(opts.parentPath, false) },
}),
)
);
}),
])
: nodes.map(n => {
return (
retroLine(ctx, n) ||
h('line', [
h('branch'),
...renderMoveAndChildrenOf(ctx, n, {
parentPath: opts.parentPath,
isMainline: false,
depth: opts.depth + 1,
withIndex: true,
truncate: n.comp && !treePath.contains(ctx.ctrl.path, opts.parentPath + n.id) ? 3 : undefined,
}),
])
);
}),
);
}

Expand All @@ -93,6 +111,7 @@ function renderMoveAndChildrenOf(ctx: Ctx, node: Tree.Node, opts: Opts): MaybeVN
renderChildrenOf(ctx, node, {
parentPath: path,
isMainline: opts.isMainline,
depth: opts.depth,
truncate: opts.truncate ? opts.truncate - 1 : undefined,
withIndex: !!comments[0],
}) || [],
Expand All @@ -104,7 +123,12 @@ function renderInline(ctx: Ctx, node: Tree.Node, opts: Opts): VNode {
if (retro) return h('interrupt', h('lines', retro));
return h(
'inline',
renderMoveAndChildrenOf(ctx, node, { withIndex: true, parentPath: opts.parentPath, isMainline: false }),
renderMoveAndChildrenOf(ctx, node, {
withIndex: true,
parentPath: opts.parentPath,
isMainline: false,
depth: opts.depth,
}),
);
}

Expand All @@ -125,6 +149,7 @@ export default function (ctrl: AnalyseCtrl): VNode {
...(renderChildrenOf(ctx, ctrl.tree.root, {
parentPath: '',
isMainline: true,
depth: 0,
}) || []),
]);
}
4 changes: 2 additions & 2 deletions ui/analyse/src/view/util.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { fixCrazySan } from 'chess';
import { attributesModule, classModule, init, h } from 'snabbdom';
import { attributesModule, classModule, eventListenersModule, init, h } from 'snabbdom';
import { plyToTurn } from '../util';

export const patch = init([classModule, attributesModule]);
export const patch = init([classModule, attributesModule, eventListenersModule]);

export const emptyRedButton = 'button.button.button-red.button-empty';

Expand Down
Loading