diff --git a/TODO b/TODO index 3510cc4..12a1496 100644 --- a/TODO +++ b/TODO @@ -3,4 +3,5 @@ - Lichess login - logger - contact form -- bring sections headers on all pages other than home page further up (reduce top margin / padding) \ No newline at end of file +- bring sections headers on all pages other than home page further up (reduce top margin / padding) +- set game current position on game load to ply 0 and whatever the FEN is (starting position probably, but could be custom position) diff --git a/src/components/PgnViewer.tsx b/src/components/PgnViewer.tsx index a7199de..89c7587 100644 --- a/src/components/PgnViewer.tsx +++ b/src/components/PgnViewer.tsx @@ -1,5 +1,4 @@ -import LichessPgnViewer from "lichess-pgn-viewer"; -import { useEffect, useRef } from "react"; +import { useLichessPgnViewer } from "#/hooks/useLichessPgnViewer.ts"; import type { IPosition } from "#/interfaces.ts"; interface IProps { @@ -8,80 +7,9 @@ interface IProps { } const PgnViewer = ({ gamePgn, handlePlyChange }: IProps) => { - const containerRef = useRef(null); - const viewerRef = useRef(null); + useLichessPgnViewer({ gamePgn, handlePlyChange }); - // TODO make thin, and pull functions out - useEffect(() => { - const element: HTMLElement | null = document.querySelector(".lpv-board"); - if (!element) return; - - viewerRef.current = LichessPgnViewer(element, { - pgn: gamePgn, - scrollToMove: false, - }); - - // set initial position - TODO change to on file load - // handlePlyChange({ - // ply: 0, - // fen: viewerRef.current?.curData().fen, - // }); - - const boardButtons = document.querySelectorAll(".lpv__controls"); - const movesList = document.querySelectorAll(".lpv__moves"); - if (!boardButtons || !movesList) return; - - const clickHandlerDelay = () => { - setTimeout(() => handleClick(), 250); - }; - - // Hack using DOM manipulation - const boardEventListener = () => { - const variationTags = document.querySelector("variation"); - const moves = [...document.querySelectorAll("move")].filter((m) => { - if (!variationTags) return m.className !== "empty"; - return ( - m.parentNode?.querySelector("variation") && m.className !== "empty" - ); - }); - const ply = moves.findIndex((m) => m.classList.contains("current")) + 1; - return { moves, ply }; - }; - - const handleClick = () => { - const { ply } = boardEventListener(); - const fen = viewerRef.current?.curData().fen; - handlePlyChange({ ply, fen }); - }; - - boardButtons.forEach((button) => { - button.addEventListener("click", handleClick); - button.addEventListener("touchstart", handleClick); - }); - - movesList.forEach((move) => { - move.addEventListener("click", clickHandlerDelay); - move.addEventListener("touchstart", clickHandlerDelay); - }); - - return () => { - if (containerRef.current) { - containerRef.current = null; - } - - boardButtons.forEach((button) => { - button.removeEventListener("click", handleClick); - button.removeEventListener("touchstart", handleClick); - }); - - movesList.forEach((move) => { - move.removeEventListener("click", clickHandlerDelay); - move.removeEventListener("touchstart", clickHandlerDelay); - }); - }; - }, [gamePgn, handlePlyChange]); - - return
; + return
; }; export default PgnViewer; diff --git a/src/hooks/useLichessPgnViewer.ts b/src/hooks/useLichessPgnViewer.ts new file mode 100644 index 0000000..b306c99 --- /dev/null +++ b/src/hooks/useLichessPgnViewer.ts @@ -0,0 +1,75 @@ +import LichessPgnViewer from "lichess-pgn-viewer"; +import { useEffect, useRef } from "react"; +import type { IPosition } from "#/interfaces.ts"; + +type ViewerRef = ReturnType; + +interface IProps { + gamePgn: string; + handlePlyChange: ({ ply, fen }: IPosition) => void; +} + +export const useLichessPgnViewer = ({ gamePgn, handlePlyChange }: IProps) => { + const viewerRef = useRef(null); + + useEffect(() => { + const element: HTMLElement | null = document.querySelector(".lpv-board"); + if (!element) return; + + viewerRef.current = LichessPgnViewer(element, { + pgn: gamePgn, + scrollToMove: false, + }); + + const boardButtons = document.querySelectorAll(".lpv__controls"); + const movesList = document.querySelectorAll(".lpv__moves"); + if (!boardButtons || !movesList) return; + + const clickHandlerDelay = () => { + setTimeout(() => handleClick(), 250); + }; + + // Hack using DOM manipulation + const boardEventListener = () => { + const variationTags = document.querySelector("variation"); + const moves = [...document.querySelectorAll("move")].filter((m) => { + if (!variationTags) return m.className !== "empty"; + return ( + m.parentNode?.querySelector("variation") && m.className !== "empty" + ); + }); + const ply = moves.findIndex((m) => m.classList.contains("current")) + 1; + return { moves, ply }; + }; + + const handleClick = () => { + const { ply } = boardEventListener(); + const fen = viewerRef.current?.curData().fen ?? ""; + handlePlyChange({ ply, fen }); + }; + + boardButtons.forEach((button) => { + button.addEventListener("click", handleClick); + button.addEventListener("touchstart", handleClick); + }); + + movesList.forEach((move) => { + move.addEventListener("click", clickHandlerDelay); + move.addEventListener("touchstart", clickHandlerDelay); + }); + + return () => { + boardButtons.forEach((button) => { + button.removeEventListener("click", handleClick); + button.removeEventListener("touchstart", handleClick); + }); + + movesList.forEach((move) => { + move.removeEventListener("click", clickHandlerDelay); + move.removeEventListener("touchstart", clickHandlerDelay); + }); + }; + }, [gamePgn, handlePlyChange]); + + return viewerRef; +};