mirror of
https://github.com/chrislicodes/obsidian-chess-study.git
synced 2026-07-22 07:50:30 +00:00
feat: style PGN Viewer
This commit is contained in:
parent
71beedae89
commit
890dd2fbbb
7 changed files with 1267 additions and 62 deletions
|
|
@ -245,7 +245,7 @@ export const ChessStudy = ({
|
|||
}, [chessStudyId, dataAdapter, gameState.study]);
|
||||
|
||||
return (
|
||||
<div className="chess-study border">
|
||||
<div className="chess-study">
|
||||
<div className="chessground-pgn-container">
|
||||
<div className="chessground-container">
|
||||
<ChessgroundWrapper
|
||||
|
|
@ -286,7 +286,7 @@ export const ChessStudy = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="CommentSection border-top">
|
||||
<div className="CommentSection">
|
||||
<CommentSection
|
||||
currentComment={gameState.currentMove?.comment}
|
||||
setComments={(comment: JSONContent) =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ArrowLeft, ArrowRight, Save } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { ReactNode, useEffect, useMemo, useRef } from 'react';
|
||||
import { ChessStudyMove } from 'src/lib/storage';
|
||||
|
||||
const chunkArray = <T,>(array: T[], chunkSize: number) => {
|
||||
|
|
@ -54,6 +54,56 @@ const MoveItem = ({
|
|||
);
|
||||
};
|
||||
|
||||
const VariantMoveItem = ({
|
||||
isCurrentMove,
|
||||
san,
|
||||
onMoveItemClick,
|
||||
moveIndicator = null,
|
||||
}: {
|
||||
isCurrentMove: boolean;
|
||||
san: string;
|
||||
onMoveItemClick: () => void;
|
||||
moveIndicator?: string | null;
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current && isCurrentMove) {
|
||||
ref.current?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
inline: 'end',
|
||||
});
|
||||
}
|
||||
}, [isCurrentMove]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`variant-move-item ${(isCurrentMove && 'active') || ''}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onMoveItemClick();
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
<span className={'variant-move-indicator'}>{moveIndicator}</span>
|
||||
{san}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const VariantMoveItemContainer = ({ children }: { children: ReactNode }) => {
|
||||
return <div className="variant-move-item-container">{children}</div>;
|
||||
};
|
||||
|
||||
const VariantContainer = ({ children }: { children: ReactNode }) => {
|
||||
return <div className="variant-container">{children}</div>;
|
||||
};
|
||||
|
||||
const VariantsContainer = ({ children }: { children: ReactNode }) => {
|
||||
return <div className="variants-container">{children}</div>;
|
||||
};
|
||||
|
||||
export const PgnViewer = React.memo(
|
||||
({
|
||||
history,
|
||||
|
|
@ -76,12 +126,14 @@ export const PgnViewer = React.memo(
|
|||
<div className="height-width-100">
|
||||
<div className="move-item-section">
|
||||
<div className="move-item-container">
|
||||
{movePairs.map((pair, i) => {
|
||||
{movePairs.map((pair, currentMoveIndex) => {
|
||||
const [wMove, bMove] = pair;
|
||||
const variants = wMove.variants.concat(bMove?.variants || []);
|
||||
|
||||
return (
|
||||
<React.Fragment key={wMove.san + bMove?.san}>
|
||||
<p className="move-indicator center">{i + 1}</p>
|
||||
<p className="move-indicator center">
|
||||
{currentMoveIndex + 1}
|
||||
</p>
|
||||
<MoveItem
|
||||
san={wMove.san}
|
||||
isCurrentMove={wMove.moveId === currentMoveId}
|
||||
|
|
@ -94,38 +146,102 @@ export const PgnViewer = React.memo(
|
|||
onMoveItemClick={() => onMoveItemClick(bMove.moveId)}
|
||||
/>
|
||||
)}
|
||||
{!!wMove.variants.concat(bMove?.variants || []).length && (
|
||||
<VariantsContainer>
|
||||
{!!wMove.variants.length && (
|
||||
<VariantContainer>
|
||||
{wMove.variants.map((variant, i) => {
|
||||
return (
|
||||
<VariantMoveItemContainer key="TODO">
|
||||
{chunkArray(variant.moves, 2).map((pair, i) => {
|
||||
const [bMove, wMove] = pair;
|
||||
|
||||
{/* Add Variants */}
|
||||
{!!variants.length && (
|
||||
<div
|
||||
style={{
|
||||
gridColumn: 'span 3',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
{variants.map((variant, i) => {
|
||||
return (
|
||||
<div key={'sd'}>
|
||||
{variant.moves.map((move) => (
|
||||
<span
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
color:
|
||||
move.moveId === currentMoveId
|
||||
? 'red'
|
||||
: 'unset',
|
||||
}}
|
||||
onClick={() => onMoveItemClick(move.moveId)}
|
||||
key={move.before}
|
||||
>
|
||||
{move.san}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<VariantMoveItem
|
||||
key="TODO"
|
||||
isCurrentMove={
|
||||
bMove.moveId === currentMoveId
|
||||
}
|
||||
san={bMove.san}
|
||||
onMoveItemClick={() =>
|
||||
onMoveItemClick(bMove.moveId)
|
||||
}
|
||||
moveIndicator={
|
||||
(i === 0 &&
|
||||
`${
|
||||
currentMoveIndex + 1 + i
|
||||
}... `) ||
|
||||
null
|
||||
}
|
||||
/>
|
||||
{wMove && (
|
||||
<VariantMoveItem
|
||||
key="TODO"
|
||||
isCurrentMove={
|
||||
wMove.moveId === currentMoveId
|
||||
}
|
||||
san={wMove.san}
|
||||
onMoveItemClick={() =>
|
||||
onMoveItemClick(wMove.moveId)
|
||||
}
|
||||
moveIndicator={
|
||||
(i % 2 === 0 && null) ||
|
||||
`${currentMoveIndex + 2 + i}. `
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</VariantMoveItemContainer>
|
||||
);
|
||||
})}
|
||||
</VariantContainer>
|
||||
)}
|
||||
{!!bMove?.variants.length && (
|
||||
<VariantContainer>
|
||||
{bMove.variants.map((variant, i) => {
|
||||
return (
|
||||
<VariantMoveItemContainer key="TODO">
|
||||
{chunkArray(variant.moves, 2).map((pair, i) => {
|
||||
const [wMove, bMove] = pair;
|
||||
return (
|
||||
<>
|
||||
<VariantMoveItem
|
||||
key="TODO"
|
||||
isCurrentMove={
|
||||
wMove.moveId === currentMoveId
|
||||
}
|
||||
san={wMove.san}
|
||||
onMoveItemClick={() =>
|
||||
onMoveItemClick(wMove.moveId)
|
||||
}
|
||||
moveIndicator={`${
|
||||
currentMoveIndex + 2 + i
|
||||
}. `}
|
||||
/>
|
||||
{bMove && (
|
||||
<VariantMoveItem
|
||||
key="TODO"
|
||||
isCurrentMove={
|
||||
bMove.moveId === currentMoveId
|
||||
}
|
||||
san={bMove.san}
|
||||
onMoveItemClick={() =>
|
||||
onMoveItemClick(bMove.moveId)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</VariantMoveItemContainer>
|
||||
);
|
||||
})}
|
||||
</VariantContainer>
|
||||
)}
|
||||
</VariantsContainer>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { DrawShape } from 'chessground/draw';
|
|||
import { nanoid } from 'nanoid';
|
||||
import { DataAdapter, normalizePath } from 'obsidian';
|
||||
|
||||
export const CURRENT_STORAGE_VERSION = '0.0.1';
|
||||
|
||||
export interface VariantMove extends Move {
|
||||
moveId: string;
|
||||
shapes: DrawShape[];
|
||||
|
|
@ -24,6 +26,7 @@ export interface ChessStudyMove extends Move {
|
|||
}
|
||||
|
||||
export interface ChessStudyFileData {
|
||||
version: string;
|
||||
header: { title: string | null };
|
||||
moves: ChessStudyMove[];
|
||||
}
|
||||
|
|
|
|||
59
src/main.css
59
src/main.css
|
|
@ -3,15 +3,6 @@
|
|||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.chess-study .border {
|
||||
border: 1px var(--color-base-10) solid;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.chess-study .border-top {
|
||||
border: 1px var(--color-base-10) solid;
|
||||
}
|
||||
|
||||
.chess-study .vertical-align {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -33,12 +24,13 @@
|
|||
}
|
||||
|
||||
.chess-study {
|
||||
height: 100%;
|
||||
max-width: 750px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.chess-study {
|
||||
height: 100%;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--background-secondary-alt);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.chess-study *,
|
||||
|
|
@ -84,6 +76,7 @@
|
|||
}
|
||||
|
||||
/* PGN Viewer */
|
||||
|
||||
.chess-study .move-item-section {
|
||||
height: 415px;
|
||||
}
|
||||
|
|
@ -97,6 +90,8 @@
|
|||
height: 35px;
|
||||
}
|
||||
|
||||
/* Move Items */
|
||||
|
||||
.chess-study .move-item-container {
|
||||
display: grid;
|
||||
grid-template-columns: 0.15fr 0.425fr 0.425fr;
|
||||
|
|
@ -131,6 +126,21 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Variant Move Items */
|
||||
|
||||
.chess-study .variants-container {
|
||||
grid-column: span 3 / auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid var(--color-base-50);
|
||||
}
|
||||
|
||||
.chess-study .variant-container {
|
||||
grid-column: span 3 / auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chess-study .move-indicator {
|
||||
color: var(--color-base-60);
|
||||
text-align: center;
|
||||
|
|
@ -139,6 +149,28 @@
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.chess-study .variant-move-item {
|
||||
padding-left: 6px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chess-study .variant-move-item-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px 0;
|
||||
border-top: 1px solid var(--color-base-50);
|
||||
}
|
||||
|
||||
.chess-study .variant-move-item.active,
|
||||
.chess-study .variant-move-item:hover {
|
||||
color: hsl(var(--accent-h), var(--accent-s), calc(var(--accent-l) - 10%));
|
||||
}
|
||||
|
||||
.chess-study .variant-move-indicator {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Comment Section */
|
||||
|
||||
.chess-study .editor-input {
|
||||
|
|
@ -157,6 +189,7 @@
|
|||
height: 250px;
|
||||
padding-top: 5px;
|
||||
overflow: scroll;
|
||||
border-top: 1px solid var(--background-secondary-alt);
|
||||
}
|
||||
|
||||
.chess-study .ProseMirror,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { Chess } from 'chess.js';
|
||||
import { Editor, Notice, Plugin, normalizePath } from 'obsidian';
|
||||
import { ChessStudyDataAdapter, ChessStudyFileData } from 'src/lib/storage';
|
||||
import {
|
||||
CURRENT_STORAGE_VERSION,
|
||||
ChessStudyDataAdapter,
|
||||
ChessStudyFileData,
|
||||
} from 'src/lib/storage';
|
||||
import { ReactView } from './components/ReactView';
|
||||
import { PgnModal } from './components/obsidian/PgnModal';
|
||||
import {
|
||||
|
|
@ -59,6 +63,7 @@ export default class ChessStudyPlugin extends Plugin {
|
|||
}
|
||||
|
||||
const chessStudyFileData: ChessStudyFileData = {
|
||||
version: CURRENT_STORAGE_VERSION,
|
||||
header: {
|
||||
title: chess.header()['opening'] || null,
|
||||
},
|
||||
|
|
|
|||
1023
storage/Qb3_uGSoPFPK8-nMPPvGx.json
Normal file
1023
storage/Qb3_uGSoPFPK8-nMPPvGx.json
Normal file
File diff suppressed because it is too large
Load diff
47
styles.css
47
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue