WIP
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.5 MiB |
@@ -83,6 +83,14 @@ class Board {
|
|||||||
this.updateConfig(config, false);
|
this.updateConfig(config, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setCanvas(canvas: HTMLCanvasElement) {
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||||
|
this.setSize(this.cfg.size);
|
||||||
|
|
||||||
|
await this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
async updateConfig(config: Partial<BoardConfig>, refresh: boolean = true) {
|
async updateConfig(config: Partial<BoardConfig>, refresh: boolean = true) {
|
||||||
const cfg = { ...this.cfg, ...config };
|
const cfg = { ...this.cfg, ...config };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Header, Style } from "../../types";
|
import { Header, Style } from "../../types";
|
||||||
import drawRectangle from "./drawRectangle";
|
import drawRectangle from "./drawRectangle";
|
||||||
import drawText from "./drawText";
|
import drawText from "./drawText";
|
||||||
|
import loadImage from "../loaders/loadImage";
|
||||||
|
|
||||||
const drawHeader = async (
|
const drawHeader = async (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
@@ -84,6 +85,25 @@ const drawHeader = async (
|
|||||||
|
|
||||||
fromTop += line;
|
fromTop += line;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const logo = await loadImage("/img/logo-full.svg");
|
||||||
|
const logoHeight = size * 0.114 * 0.5;
|
||||||
|
|
||||||
|
ctx.shadowColor = "rgba(0, 0, 0, 1)";
|
||||||
|
ctx.shadowOffsetX = 0;
|
||||||
|
ctx.shadowOffsetY = 0;
|
||||||
|
ctx.shadowBlur = 10 * scale;
|
||||||
|
ctx.drawImage(
|
||||||
|
logo,
|
||||||
|
size / 4,
|
||||||
|
margin + size + margin - logoHeight * 2,
|
||||||
|
size / 2,
|
||||||
|
logoHeight
|
||||||
|
);
|
||||||
|
ctx.shadowColor = "rgba(0, 0, 0, 0)";
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
ctx.shadowOffsetX = 0;
|
||||||
|
ctx.shadowOffsetY = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default drawHeader;
|
export default drawHeader;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#3cff0055",
|
data: "#ffaa0055",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#3cff0055",
|
data: "#00ffff55",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#3cff0055",
|
data: "#ffaa0055",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#00ffee55",
|
data: "#ffff0055",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#3cff0055",
|
data: "#ff00ff55",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#00ffee55",
|
data: "#00ffff55",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#3cff0055",
|
data: "#ffaa0055",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "gradient",
|
type: "gradient",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#ffff0055",
|
data: "#ff000044",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "solid",
|
type: "solid",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const style: Style = {
|
|||||||
},
|
},
|
||||||
moveIndicator: {
|
moveIndicator: {
|
||||||
type: "color",
|
type: "color",
|
||||||
data: "#ffff0055",
|
data: "#00ffff55",
|
||||||
},
|
},
|
||||||
border: {
|
border: {
|
||||||
type: "solid",
|
type: "solid",
|
||||||
|
|||||||
28
src/imports/importToLichess.ts
Normal file
28
src/imports/importToLichess.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const importToLichess = async (pgn: string, site: string | null) => {
|
||||||
|
if (site && /^https:\/\/lichess\.org\/.{8}/.test(site)) {
|
||||||
|
return site;
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryString = new URLSearchParams({ pgn }).toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch("https://lichess.org/api/import", {
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: queryString,
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
const data = await res.json();
|
||||||
|
return data.url;
|
||||||
|
} else {
|
||||||
|
throw new Error("Cannot import to lichess");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("Cannot import to lichess");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default importToLichess;
|
||||||
26
src/main.tsx
26
src/main.tsx
@@ -22,6 +22,7 @@ import isPGN from "./utils/isPGN";
|
|||||||
import isSafeLink from "./utils/isSafeLink";
|
import isSafeLink from "./utils/isSafeLink";
|
||||||
import { PiecesStyle } from "./board/styles-pieces/piecesStyles";
|
import { PiecesStyle } from "./board/styles-pieces/piecesStyles";
|
||||||
import link from "./persistance/link";
|
import link from "./persistance/link";
|
||||||
|
import importToLichess from "./imports/importToLichess";
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
const board = new Board(state.boardConfig);
|
const board = new Board(state.boardConfig);
|
||||||
@@ -232,8 +233,27 @@ const main = async () => {
|
|||||||
setState("boardConfig", "speech", !state.boardConfig.speech);
|
setState("boardConfig", "speech", !state.boardConfig.speech);
|
||||||
saveConfig("board");
|
saveConfig("board");
|
||||||
},
|
},
|
||||||
|
async openOnLichess() {
|
||||||
|
if (state.pgn === "") {
|
||||||
|
window.open(
|
||||||
|
`https://lichess.org/analysis/${state.fen.replace(/\s+/g, "_")}`
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = await importToLichess(state.pgn, state.game.header.Site);
|
||||||
|
window.open(`${url}/${state.boardConfig.flipped ? "black" : ""}`);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.handlers = handlers;
|
||||||
|
|
||||||
/* Render the page */
|
/* Render the page */
|
||||||
|
|
||||||
render(
|
render(
|
||||||
@@ -241,8 +261,10 @@ const main = async () => {
|
|||||||
document.getElementById("root") as HTMLElement
|
document.getElementById("root") as HTMLElement
|
||||||
);
|
);
|
||||||
|
|
||||||
const $board = document.querySelector<HTMLImageElement>("#board");
|
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||||
$board?.prepend(board.canvas);
|
|
||||||
|
await board.setCanvas(canvas);
|
||||||
|
await player.init();
|
||||||
|
|
||||||
/* Load game from the url */
|
/* Load game from the url */
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,18 @@ class Player {
|
|||||||
private speech: Speech;
|
private speech: Speech;
|
||||||
public playing: boolean = false;
|
public playing: boolean = false;
|
||||||
|
|
||||||
private firstRender: Promise<void>;
|
// private firstRender: Promise<void>;
|
||||||
|
|
||||||
constructor(private board: Board, private config: GameConfig) {
|
constructor(private board: Board, private config: GameConfig) {
|
||||||
this.speech = new Speech();
|
this.speech = new Speech();
|
||||||
|
|
||||||
this.firstRender = this.board
|
// this.firstRender = this.board
|
||||||
|
// .frame(this.game.getPosition(0), this.game.header)
|
||||||
|
// .then((_) => this.board.render());
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.board
|
||||||
.frame(this.game.getPosition(0), this.game.header)
|
.frame(this.game.getPosition(0), this.game.header)
|
||||||
.then((_) => this.board.render());
|
.then((_) => this.board.render());
|
||||||
}
|
}
|
||||||
@@ -43,7 +49,7 @@ class Player {
|
|||||||
|
|
||||||
async load(game: Game) {
|
async load(game: Game) {
|
||||||
this.pause();
|
this.pause();
|
||||||
await this.firstRender;
|
// await this.firstRender;
|
||||||
|
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.ply = 0;
|
this.ply = 0;
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ export type Handlers = {
|
|||||||
downloadAnimation: () => Promise<void>;
|
downloadAnimation: () => Promise<void>;
|
||||||
toggleSound(): void;
|
toggleSound(): void;
|
||||||
toggleSpeech(): void;
|
toggleSpeech(): void;
|
||||||
|
openOnLichess: () => Promise<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Header = {
|
export type Header = {
|
||||||
|
|||||||
@@ -94,6 +94,11 @@ button:hover,
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn--error,
|
||||||
|
.btn--error:hover {
|
||||||
|
background-color: #fc4444;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -155,6 +160,10 @@ a:hover {
|
|||||||
padding: var(--header-margin) 0 2rem 0;
|
padding: var(--header-margin) 0 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-box--left {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.board {
|
.board {
|
||||||
box-shadow: 0 0 2rem #00000099;
|
box-shadow: 0 0 2rem #00000099;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|||||||
@@ -23,7 +23,14 @@ const App: Component<{ handlers: Handlers; state: DeepReadonly<State> }> = (
|
|||||||
<SetupTabs handlers={props.handlers}></SetupTabs>
|
<SetupTabs handlers={props.handlers}></SetupTabs>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<div id="board" class="board-box">
|
<div
|
||||||
|
id="board"
|
||||||
|
classList={{
|
||||||
|
"board-box": true,
|
||||||
|
"board-box--left": state.layout === "double",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<canvas class="board" id="canvas"></canvas>
|
||||||
<Show when={state.layout === "single"}>
|
<Show when={state.layout === "single"}>
|
||||||
<Controls handlers={props.handlers} />
|
<Controls handlers={props.handlers} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
background: var(--color-bg-block);
|
background: var(--color-bg-block);
|
||||||
padding: 2rem;
|
padding: 2rem 1rem;
|
||||||
border-bottom-left-radius: 5px;
|
border-bottom-left-radius: 5px;
|
||||||
border-bottom-right-radius: 5px;
|
border-bottom-right-radius: 5px;
|
||||||
}
|
}
|
||||||
@@ -85,3 +85,9 @@
|
|||||||
max-height: 6rem;
|
max-height: 6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (orientation: landscape) and (max-height: 400px) {
|
||||||
|
.controls {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
.game {
|
.game {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 3.8rem 19.5rem 1fr 8.4rem;
|
grid-template-rows: 3.8rem 22rem auto 8.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-tabs {
|
.game-tabs {
|
||||||
@@ -42,3 +42,9 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (orientation: landscape) and (max-height: 400px) {
|
||||||
|
.game {
|
||||||
|
grid-template-rows: 3.8rem 1fr 6.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
|
|
||||||
.info {
|
.info {
|
||||||
background: var(--color-bg-block);
|
background: var(--color-bg-block);
|
||||||
padding: 3rem 2rem;
|
padding: 2rem;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info__players {
|
.info__players {
|
||||||
@@ -65,3 +66,10 @@
|
|||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info__analyze {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 2rem;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 4rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Component, Show } from "solid-js";
|
import { Component, Show, createSignal } from "solid-js";
|
||||||
import { Handlers } from "../../types";
|
import { Handlers } from "../../types";
|
||||||
import { state } from "../../state";
|
import { state } from "../../state";
|
||||||
import "./Info.css";
|
import "./Info.css";
|
||||||
import isSafeLink from "../../utils/isSafeLink";
|
import isSafeLink from "../../utils/isSafeLink";
|
||||||
import isLink from "../../utils/isLink";
|
import isLink from "../../utils/isLink";
|
||||||
|
|
||||||
const Info: Component<{ handlers: Handlers }> = () => {
|
const Info: Component<{ handlers: Handlers }> = (props) => {
|
||||||
|
const [error, setError] = createSignal(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div className="info__players">
|
<div className="info__players">
|
||||||
@@ -83,22 +85,26 @@ const Info: Component<{ handlers: Handlers }> = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
</p>
|
</p>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={state.pgn === ""}>
|
|
||||||
<p>
|
|
||||||
<a
|
|
||||||
href={`https://lichess.org/analysis/${state.fen.replace(
|
|
||||||
/\s+/g,
|
|
||||||
"_"
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
Analyze on Lichess
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</Show>
|
|
||||||
<Show when={state.game.header.DatePretty}>
|
<Show when={state.game.header.DatePretty}>
|
||||||
<p>{state.game.header.DatePretty}</p>
|
<p>{state.game.header.DatePretty}</p>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="info__analyze">
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
const success = await props.handlers.openOnLichess();
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
setError(true);
|
||||||
|
setTimeout(() => setError(false), 1000);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
classList={{ "btn--error": error() }}
|
||||||
|
>
|
||||||
|
<i className="las la-vial"></i>{" "}
|
||||||
|
{error() ? "Cannot import to lichess" : "Analyze on Lichess"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
.load {
|
.load {
|
||||||
background: var(--color-bg-block);
|
background: var(--color-bg-block);
|
||||||
padding: 2rem;
|
|
||||||
border-bottom-left-radius: 5px;
|
border-bottom-left-radius: 5px;
|
||||||
border-bottom-right-radius: 5px;
|
border-bottom-right-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.load__game-input {
|
.load__game-input {
|
||||||
height: 50vh;
|
height: 40vh;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,3 +43,15 @@
|
|||||||
.load__link-input {
|
.load__link-input {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (orientation: portrait) {
|
||||||
|
.load__game-input {
|
||||||
|
height: 20vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (orientation: landscape) and (max-height: 400px) {
|
||||||
|
.load__game-input {
|
||||||
|
height: 30vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,35 @@
|
|||||||
import { Component, createSignal, Show } from "solid-js";
|
import { Component, createSignal, Show } from "solid-js";
|
||||||
import { Handlers } from "../../types";
|
import { Handlers } from "../../types";
|
||||||
import readFile from "../../utils/readFile";
|
import readFile from "../../utils/readFile";
|
||||||
|
import Scrollable from "./reusable/Scrollable";
|
||||||
import { setState, state } from "../../state";
|
import { setState, state } from "../../state";
|
||||||
import "./Load.css";
|
import "./Load.css";
|
||||||
|
|
||||||
const Load: Component<{ handlers: Handlers; class?: string }> = (props) => {
|
const Load: Component<{ handlers: Handlers; class?: string }> = (props) => {
|
||||||
const [data, setData] = createSignal("");
|
const [data, setData] = createSignal("");
|
||||||
|
const [clipError, setClipError] = createSignal(false);
|
||||||
|
const [inputError, setInputError] = createSignal(false);
|
||||||
|
|
||||||
let filePicker: HTMLInputElement | undefined = undefined;
|
let filePicker: HTMLInputElement | undefined = undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={"load" + (props.class ? ` ${props.class}` : "")}>
|
<Scrollable class={"load" + (props.class ? ` ${props.class}` : "")}>
|
||||||
|
<button
|
||||||
|
classList={{ "load__game-btn": true, "btn--error": clipError() }}
|
||||||
|
onClick={async () => {
|
||||||
|
const clip = await navigator.clipboard.readText();
|
||||||
|
const success = await props.handlers.load(clip);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
setClipError(true);
|
||||||
|
setTimeout(() => setClipError(false), 1000);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i class="las la-paste"></i>{" "}
|
||||||
|
{clipError() ? "Incorrect data" : "Load from clipboard"}
|
||||||
|
</button>
|
||||||
|
<hr />
|
||||||
<textarea
|
<textarea
|
||||||
class="load__game-input"
|
class="load__game-input"
|
||||||
name="load-game"
|
name="load-game"
|
||||||
@@ -20,15 +39,18 @@ const Load: Component<{ handlers: Handlers; class?: string }> = (props) => {
|
|||||||
onInput={(e) => setData(e.currentTarget.value)}
|
onInput={(e) => setData(e.currentTarget.value)}
|
||||||
></textarea>
|
></textarea>
|
||||||
<button
|
<button
|
||||||
class="load__game-btn"
|
classList={{ "load__game-btn": true, "btn--error": inputError() }}
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
if (data()) {
|
const success = await props.handlers.load(data());
|
||||||
props.handlers.load(data());
|
|
||||||
setData("");
|
if (!success) {
|
||||||
|
setInputError(true);
|
||||||
|
setTimeout(() => setInputError(false), 1000);
|
||||||
}
|
}
|
||||||
|
setData("");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
LOAD
|
{inputError() ? "Incorrect data" : "LOAD"}
|
||||||
</button>
|
</button>
|
||||||
<hr />
|
<hr />
|
||||||
<input
|
<input
|
||||||
@@ -53,7 +75,7 @@ const Load: Component<{ handlers: Handlers; class?: string }> = (props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
UPLOAD PGN FILE
|
Upload PGN file
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Show when={!state.mobile}>
|
<Show when={!state.mobile}>
|
||||||
@@ -62,7 +84,7 @@ const Load: Component<{ handlers: Handlers; class?: string }> = (props) => {
|
|||||||
<p>drop the PGN file anywhere on the page</p>
|
<p>drop the PGN file anywhere on the page</p>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</Scrollable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,3 +51,9 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (orientation: landscape) and (max-height: 400px) {
|
||||||
|
.moves {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user