study board editor WIP

more-scalatags
Thibault Duplessis 2019-04-10 16:35:48 +07:00
parent 4e9a0c6236
commit 165581d195
14 changed files with 108 additions and 68 deletions

View File

@ -29,44 +29,44 @@ lichess.studyTourChapter = function(study) {
text: "A study can have several chapters.<br>" +
"Each chapter has a distinct move tree,<br>" +
"and can be created in various ways.",
attachTo: '.study_overboard label[for=chapter-name] left'
attachTo: '.study__modal 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_overboard .study_tabs .init top',
attachTo: '.study__modal .tabs-horiz .init top',
when: onTab('init')
}, {
title: "Custom position",
text: "Setup the board your way.<br>" +
"Suited to explore endgames.",
attachTo: '.study_overboard .study_tabs .edit bottom',
attachTo: '.study__modal .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_overboard .study_tabs .game top',
attachTo: '.study__modal .tabs-horiz .game top',
when: onTab('game')
}, {
title: "From a FEN string",
text: "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_overboard .study_tabs .fen top',
attachTo: '.study__modal .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_overboard .study_tabs .pgn top',
attachTo: '.study__modal .tabs-horiz .pgn top',
when: onTab('pgn')
}, {
title: "Studies support variants",
text: "Yes, you can study crazyhouse,<br>" +
"and all lichess variants!",
attachTo: '.study_overboard label[for=chapter-variant] left',
attachTo: '.study__modal label[for=chapter-variant] left',
when: onTab('init')
}, {
title: "Thanks for your time",
@ -76,7 +76,7 @@ lichess.studyTourChapter = function(study) {
text: 'Done',
action: tour.next
}],
attachTo: '.study_overboard .help bottom'
attachTo: '.study__modal .help bottom'
}].forEach(function(s) {
tour.addStep(s.title, s);
});

View File

@ -13,7 +13,6 @@
@import 'explorer';
@import 'training';
@import 'practice';
@import 'retro';
@import 'fork';
@import 'side';
// @import 'side-clock';

View File

@ -1,5 +1,6 @@
/* analyse a game replay */
@import 'analyse.base';
@import 'retro';
@import 'acpl';
$col1-panel-height: 30vh;

View File

@ -1,4 +1,5 @@
@import 'analyse.base';
@import '../../../common/css/form/form3';
@import '../../../common/css/component/tabs-horiz';
@import '../study/show';

View File

@ -0,0 +1,33 @@
.board-editor-wrap {
min-height: 280px;
margin-bottom: 2em;
.spinner {
padding-top: 90px;
}
}
.board-editor {
display: grid;
grid-template-columns: 300px 2vmin 200px;
grid-template-rows: min-content auto min-content;
grid-template-areas:
'. . e-tools'
'e-spare-top . e-tools'
'e-board . e-tools'
'e-spare-bottom . e-tools'
'. . e-tools';
.main-board {
grid-area: e-board;
}
&__tools {
grid-area: e-tools;
}
&-top {
grid-area: spare-top;
margin-bottom: .5em;
}
&-bottom {
grid-area: spare-bottom;
margin-top: .5em;
}
}

View File

@ -11,11 +11,15 @@
h2 {
margin-bottom: .5em;
i {
margin-left: 10px;
opacity: 0.5;
font-size: .6em;
&, &::before {
vertical-align: top;
}
margin-left: 1rem;
opacity: .5;
cursor: pointer;
&:hover {
opacity: 0.7;
opacity: .7;
color: $c-primary;
}
}
@ -33,4 +37,8 @@
}
}
}
&.chapter-new .tabs-horiz {
margin: -1em 0 1.6em 0;
}
}

View File

@ -6,3 +6,4 @@
@import 'members';
@import 'player';
@import 'modal';
@import 'editor';

View File

@ -3,7 +3,10 @@
justify-content: center;
align-items: flex-end;
border-bottom: 2px solid $c-border;
a {
@include breakpoint($mq-not-xx-small) {
font-size: .9em;
}
span {
@extend %roboto;
flex: 1 1 auto;
text-align: center;

View File

@ -11,10 +11,10 @@ import { title as descTitle } from './chapterDescription';
import AnalyseCtrl from '../ctrl';
export const modeChoices = [
['normal', "Normal analysis"],
['practice', "Practice with computer"],
['conceal', "Hide next moves"],
['gamebook', "Interactive lesson"]
['normal', 'Normal analysis'],
['practice', 'Practice with computer'],
['conceal', 'Hide next moves'],
['gamebook', 'Interactive lesson']
];
export function fieldValue(e: Event, id: string) {
@ -28,9 +28,9 @@ export function ctrl(send: SocketSend, chapters: Prop<StudyChapterMeta[]>, setTa
const vm = {
variants: [],
open: false,
open: true,
initial: prop(false),
tab: storedProp('study.form.tab', 'init'),
tab: storedProp('study.form.tab', 'editor'),
editor: null,
editorFen: prop(null)
};
@ -86,7 +86,7 @@ export function view(ctrl): VNode {
const activeTab = ctrl.vm.tab();
const makeTab = function(key: string, name: string, title: string) {
return h('a.' + key, {
return h('span.' + key, {
class: { active: activeTab === key },
attrs: { title },
hook: bind('click', () => ctrl.vm.tab(key), ctrl.root.redraw)
@ -109,7 +109,7 @@ export function view(ctrl): VNode {
hook: bind('click', ctrl.startTour)
})
]),
h('form.chapter_form.material.form', {
h('form.form3', {
hook: bindSubmit(e => {
const o: any = {
fen: fieldValue(e, 'fen') || (ctrl.vm.tab() === 'edit' ? ctrl.vm.editorFen() : null)
@ -121,7 +121,10 @@ export function view(ctrl): VNode {
}, ctrl.redraw)
}, [
h('div.form-group', [
h('input#chapter-name', {
h('label.form-label', {
attrs: {for: 'chapter-name' }
}, 'Name'),
h('input#chapter-name.form-control', {
attrs: {
minlength: 2,
maxlength: 80
@ -133,20 +136,16 @@ export function view(ctrl): VNode {
el.focus();
}
})
}),
h('label.control-label', {
attrs: {for: 'chapter-name' }
}, 'Name'),
h('i.bar')
})
]),
h('div.study_tabs', [
makeTab('init', 'Init', 'Start from initial position'),
makeTab('edit', 'Edit', 'Start from custom position'),
h('div.tabs-horiz', [
makeTab('init', 'Empty', 'Start from initial position'),
makeTab('edit', 'Editor', 'Start from custom position'),
makeTab('game', 'URL', 'Load a game URL'),
makeTab('fen', 'FEN', 'Load a FEN position'),
makeTab('pgn', 'PGN', 'Load a PGN game')
]),
activeTab === 'edit' ? h('div.editor_wrap.is2d', {
activeTab === 'edit' ? h('div.board-editor-wrap.is2d', {
hook: {
insert: vnode => {
$.when(
@ -171,29 +170,26 @@ export function view(ctrl): VNode {
}
}, [spinner()]) : null,
activeTab === 'game' ? h('div.form-group', [
h('input#chapter-game', {
attrs: { placeholder: 'URL of the game' }
}),
h('label.control-label', {
h('label.form-label', {
attrs: { 'for': 'chapter-game' }
}, 'Load a game from lichess.org or chessgames.com'),
h('i.bar')
h('input#chapter-game.form-control', {
attrs: { placeholder: 'URL of the game' }
})
]) : null,
activeTab === 'fen' ? h('div.form-group.no-label', [
h('input#chapter-fen', {
activeTab === 'fen' ? h('div.form-group', [
h('input#chapter-fen.form-control', {
attrs: {
value: ctrl.root.node.fen,
placeholder: 'Initial FEN position'
}
}),
h('i.bar')
})
]) : null,
activeTab === 'pgn' ? h('div.form-group.no-label', [
h('textarea#chapter-pgn', {
activeTab === 'pgn' ? h('div.form-groupabel', [
h('textarea#chapter-pgn.form-control', {
attrs: { placeholder: 'Paste your PGN text here, up to ' + ctrl.multiPgnMax + ' games' }
}),
h('i.bar'),
window.FileReader ? h('input#chapter-pgn-file', {
window.FileReader ? h('input#chapter-pgn-file.form-control', {
attrs: {
type: 'file',
accept: '.pgn'
@ -209,40 +205,37 @@ export function view(ctrl): VNode {
})
}) : null
]) : null,
h('div', [
h('div.form-group.half.little-margin-bottom', [
h('select#chapter-variant', {
h('div.form-split', [
h('div.form-group.form-half', [
h('label.form-label', {
attrs: { 'for': 'chapter-variant' }
}, 'Variant'),
h('select#chapter-variant.form-control', {
attrs: { disabled: gameOrPgn }
}, gameOrPgn ? [
h('option', 'Automatic')
] :
ctrl.vm.variants.map(v => option(v.key, currentChapterSetup.variant.key, v.name))),
h('label.control-label', {
attrs: { 'for': 'chapter-variant' }
}, 'Variant'),
h('i.bar')
ctrl.vm.variants.map(v => option(v.key, currentChapterSetup.variant.key, v.name)))
]),
h('div.form-group.half.little-margin-bottom', [
h('select#chapter-orientation', {
h('div.form-group.form-half', [
h('label.form-label', {
attrs: { 'for': 'chapter-orientation' }
}, 'Orientation'),
h('select#chapter-orientation.form-control', {
hook: bind('change', e => {
ctrl.vm.editor && ctrl.vm.editor.setOrientation((e.target as HTMLInputElement).value);
})
}, ['White', 'Black'].map(function(color) {
const c = color.toLowerCase();
return option(c, currentChapterSetup.orientation, color);
})),
h('label.control-label', {
attrs: { 'for': 'chapter-orientation' }
}, 'Orientation'),
h('i.bar')
}))
])
]),
h('div.form-group.little-margin-bottom', [
h('select#chapter-mode', modeChoices.map(c => option(c[0], '', c[1]))),
h('label.control-label', {
h('div.form-group', [
h('label.form-label', {
attrs: { 'for': 'chapter-mode' }
}, 'Analysis mode'),
h('i.bar')
h('select#chapter-mode.form-control', modeChoices.map(c => option(c[0], '', c[1])))
]),
dialog.button('Create chapter')
])

View File

@ -145,13 +145,13 @@ export function side(ctrl: StudyCtrl): VNode {
const activeTab = ctrl.vm.tab();
const makeTab = function(key: Tab, name: string) {
return h('a.' + key, {
return h('span.' + key, {
class: { active: activeTab === key },
hook: bind('mousedown', () => ctrl.vm.tab(key), ctrl.redraw)
}, name);
};
const tabs = h('div.study__tabs', [
const tabs = h('div.tabs-horiz', [
makeTab('chapters', plural(ctrl.relay ? 'Game' : 'Chapter', ctrl.chapters.size())),
makeTab('members', plural('Member', ctrl.members.size())),
ctrl.members.isOwner() ? h('a.more', {

View File

@ -5,7 +5,7 @@
}
&-wrap {
@extend %box-radius, %popup-shadow, %flex-column;
background: $c-bg-popup;
background: $c-bg-box;
position: relative;
padding: 2rem;
text-align: center;

View File

@ -1,4 +1,4 @@
.lobby__app__tabs {
.tabs-horiz {
@extend %flex-center-nowrap;
justify-content: center;
align-items: flex-end;

View File

@ -3,5 +3,6 @@
@import '../../../common/css/component/now-playing';
@import '../../../common/css/component/color-icon';
@import '../../../common/css/component/glowing';
@import '../../../common/css/component/tabs-horiz';
@import '../../../common/css/base/scrollbar';
@import '../lobby';

View File

@ -27,7 +27,7 @@ export default function(ctrl: LobbyController) {
break;
}
return h('div.lobby__app.lobby__app-' + ctrl.tab, [
h('div.lobby__app__tabs', renderTabs(ctrl)),
h('div.tabs-horiz', renderTabs(ctrl)),
h('div.lobby__app__content.l' + (ctrl.redirecting ? 'redir' : ctrl.tab), data, body)
]);
};