first commit

This commit is contained in:
unknown
2023-01-27 20:50:01 +08:00
commit a5e17d8c5d
69 changed files with 12547 additions and 0 deletions

View File

@ -0,0 +1,3 @@
export function BottomBannerAdvert() {
return <div id="ezoic-pub-ad-placeholder-130" />;
}

View File

@ -0,0 +1,3 @@
export function LeftSidebarAdvert() {
return <div id="ezoic-pub-ad-placeholder-133" />;
}

View File

@ -0,0 +1,3 @@
export function RightSidebarAdvert() {
return <div id="ezoic-pub-ad-placeholder-132" />;
}

View File

@ -0,0 +1,3 @@
export function TopBannerAdvert() {
return <div id="ezoic-pub-ad-placeholder-124" />;
}

View File

@ -0,0 +1,61 @@
import { Text, VStack } from '@chakra-ui/react';
export function DraughtsRulesContent() {
return (
<VStack align="start" spacing={2}>
<Text>
Permainan draf dimainkan di papan catur 64 persegi dengan delapan
deretan kotak berwarna gelap dan terang bergantian.
</Text>
<Text>
Ada dua pemain dan masing-masing memulai permainan dengan masing-masing 12 draf
pemain memiliki warna mereka sendiri.
</Text>
<Text>
Para pemain menempatkan draf mereka di tiga baris kotak gelap yang
paling dekat dengan mereka.
</Text>
<Text>Para pemain kemudian mulai bermain, melakukan satu gerakan pada satu waktu.</Text>
<Text>
Tujuan permainan ini adalah membuat lawan tidak bisa bergerak
ketika tiba giliran mereka.
</Text>
<Text>
Ini dilakukan dengan mengambil semua bidak mereka sepanjang permainan, atau
memblokir mereka sehingga mereka tidak punya tempat untuk bergerak.
</Text>
<Text>
Draf tunggal hanya dapat bergerak dalam arah diagonal ke depan ke a
persegi tanpa bagian lain di dalamnya.
</Text>
<Text>
Jika bidak lawan ada di kotak berikutnya, pemain bisa melompat
di atasnya dan tangkap, lepaskan potongan itu dari papan. Mereka hanya bisa
lakukan ini jika kotak berikutnya kosong.
</Text>
<Text>Pemain tidak pernah bisa melompati bagian mereka sendiri.</Text>
<Text>
Ketika seorang pemain berjalan melintasi papan ke yang lain
sisi pemain, bidak mereka akan berubah menjadi Raja. Ketika ini terjadi,
salah satu bidak yang diambil sebelumnya akan diletakkan di atas bidak
yang membuatnya ke sisi itu.
</Text>
<Text>
Setelah sepotong dibuat menjadi raja, itu akan dapat bergerak maju dan
mundur, memberikan lebih banyak kesempatan untuk menangkap bagian lawan.
</Text>
<Text>
Seorang raja dapat melompat sebanyak mungkin sehubungan dengan
kotak yang diperlukan sedang kosong. Namun, raja tidak bisa melompati
potongan-potongan yang memiliki warna yang sama dengan mereka.
</Text>
<Text>
Permainan akan berakhir setelah pemain tidak bisa lagi bergerak.
</Text>
<Text>
Jika kedua pemain tidak bisa bergerak kemana-mana, permainan akan berakhir dengan a
mengikat, atau menggambar.
</Text>
</VStack>
);
}

View File

@ -0,0 +1,26 @@
import PropTypes from 'prop-types';
import {
DraughtsSettingsProvider,
DraughtsSettingsProviderProps,
} from './settings/DraughtsSettingsContext';
import {
DraughtsBoardProvider,
DraughtsBoardProviderProps,
} from './board/DraughtsBoardContext';
import { DraughtsGameProvider } from './game/DraughtsGameContext';
export function DraughtsProvider(props) {
return (
<DraughtsSettingsProvider {...props.settings}>
<DraughtsBoardProvider {...props.board}>
<DraughtsGameProvider>{props.children}</DraughtsGameProvider>
</DraughtsBoardProvider>
</DraughtsSettingsProvider>
);
}
DraughtsProvider.propTypes = {
board: PropTypes.shape(DraughtsBoardProviderProps),
children: PropTypes.node.isRequired,
settings: PropTypes.shape(DraughtsSettingsProviderProps),
};

View File

@ -0,0 +1,59 @@
import { HStack, VStack, Text, keyframes } from '@chakra-ui/react';
import { useDraughtsPlayerToMove } from './board/hooks/use-draughts-player-to-move';
import { useDraughtsGame } from './game/DraughtsGameContext';
import { useDraughtsSettings } from './settings/DraughtsSettingsContext';
import { Players } from '@draughts/core';
const glowKeyframe = keyframes`
from {
background-color: rgba(144, 238, 144, 0.3)
}
to {
background-color: rgba(144, 238, 144, 0.6)
}
`;
const glowAnimation = `${glowKeyframe} 1s ease 0s infinite alternate`;
function formatTimer({ seconds, minutes }) {
const formatSeconds = String(seconds).padStart(2, '0');
return `${minutes}:${formatSeconds}`;
}
export function DraughtsGameInfoView() {
const { userPlayer } = useDraughtsSettings();
const { whiteTimer, blackTimer } = useDraughtsGame();
const whiteToMove = useDraughtsPlayerToMove(Players.WHITE);
const blackToMove = useDraughtsPlayerToMove(Players.BLACK);
return (
<VStack justify="space-between">
<HStack
align="center"
justify="space-between"
gap={3}
px={2}
borderRadius="0.2em"
animation={whiteToMove ? glowAnimation : undefined}
>
<Text fontSize="sm">
putih {userPlayer === Players.WHITE ? '(Anda)' : '(AI)'}
</Text>
<time>{formatTimer(whiteTimer)}</time>
</HStack>
<HStack
align="center"
justify="space-between"
gap={3}
px={2}
borderRadius="0.2em"
animation={blackToMove ? glowAnimation : undefined}
>
<Text fontSize="sm">
hitam {userPlayer === Players.BLACK ? '(Anda)' : '(AI)'}
</Text>
<time>{formatTimer(blackTimer)}</time>
</HStack>
</VStack>
);
}

View File

@ -0,0 +1,51 @@
import { createContext, useCallback, useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { Board, Pieces, Players } from '@draughts/core';
export const DraughtsBoardContext = createContext();
export const useDraughtsBoard = () => useContext(DraughtsBoardContext);
export function DraughtsBoardProvider(props) {
const [lastMove, setLastMove] = useState(null);
const [board, setBoard] = useState(
new Board(props.position, props.playerToMove)
);
const doMove = useCallback((move) => {
setBoard((board) => {
return board.doMove(move);
});
setLastMove(move.path.at(-1));
}, []);
const resetBoard = useCallback(() => {
setBoard(new Board(props.position, props.playerToMove));
setLastMove(null);
}, [props.position, props.playerToMove]);
return (
<DraughtsBoardContext.Provider
value={{
board,
doMove,
lastMove,
resetBoard,
}}
>
{props.children}
</DraughtsBoardContext.Provider>
);
}
export const DraughtsBoardProviderProps = {
playerToMove: PropTypes.oneOf(Object.values(Players)),
position: PropTypes.arrayOf(
PropTypes.arrayOf(PropTypes.oneOf(Object.values(Pieces)))
).isRequired,
};
DraughtsBoardProvider.propTypes = {
children: PropTypes.node.isRequired,
...DraughtsBoardProviderProps,
};

View File

@ -0,0 +1,6 @@
import { useDraughtsBoard } from '../DraughtsBoardContext';
export function useDraughtsPlayerToMove(player) {
const { board } = useDraughtsBoard();
return player === board.playerToMove;
}

View File

@ -0,0 +1,17 @@
import { useMemo } from 'react';
import { useDraughtsBoard } from '../DraughtsBoardContext';
import { useDraughtsSettings } from '../../settings/DraughtsSettingsContext';
import { Players, GameStates } from '@draughts/core';
export function useDraughtsWinner() {
const { board } = useDraughtsBoard();
const { userPlayer } = useDraughtsSettings();
const winner = useMemo(() => {
if (board.state === GameStates.WHITE_WON) return Players.WHITE;
if (board.state === GameStates.BLACK_WON) return Players.BLACK;
return Players.NONE;
}, [board.state]);
return { userWon: winner === userPlayer, winner };
}

View File

@ -0,0 +1,65 @@
import { useMemo } from 'react';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
import { Grid, GridItem } from '@chakra-ui/react';
import { TouchBackend } from 'react-dnd-touch-backend';
import { useDraughtsBoard } from '../DraughtsBoardContext';
import { useDraughtsSettings } from '../../settings/DraughtsSettingsContext';
import { DraughtsCell } from './DraughtsCell';
import { DraughtsGameOverModal } from './DraughtsGameOverModal';
import { Players } from '@draughts/core';
function isTouchDevice() {
return (
'ontouchstart' in window ||
navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0
);
}
export function DraughtsBoard() {
const { board } = useDraughtsBoard();
const { userPlayer } = useDraughtsSettings();
const backend = useMemo(() => {
if (typeof window === 'undefined') return HTML5Backend;
return isTouchDevice() ? TouchBackend : HTML5Backend;
}, []);
const rows = useMemo(() => {
const entries = board.position.map((row, rowIndex) => ({
row,
rowIndex,
}));
if (userPlayer === Players.WHITE) {
return entries;
}
return entries.reverse();
}, [userPlayer, board.position]);
return (
<DndProvider backend={backend}>
<DraughtsGameOverModal />
<Grid
templateRows="repeat(8, 1fr)"
templateColumns="repeat(8, 1fr)"
w="100%"
h="100%"
style={{ aspectRatio: 1 }}
>
{rows.map(({ row, rowIndex }) =>
row.map((piece, colIndex) => (
// eslint-disable-next-line react/no-array-index-key
<GridItem key={`${rowIndex}:${colIndex}:${piece}`}>
<DraughtsCell
piece={piece}
rowIndex={rowIndex}
colIndex={colIndex}
/>
</GridItem>
))
)}
</Grid>
</DndProvider>
);
}

View File

@ -0,0 +1,78 @@
import PropTypes from 'prop-types';
import { useDrop } from 'react-dnd';
import { Box, Square } from '@chakra-ui/react';
import { useDraughtsBoard } from '../DraughtsBoardContext';
import { DraughtsPiece } from './DraughtsPiece';
import { compareCells, Pieces } from '@draughts/core';
DraughtsCell.propTypes = {
colIndex: PropTypes.number.isRequired,
piece: PropTypes.oneOf(Object.values(Pieces)).isRequired,
rowIndex: PropTypes.number.isRequired,
};
export function DraughtsCell(props) {
const { board, doMove, lastMove } = useDraughtsBoard();
const isDark = (props.rowIndex + props.colIndex) % 2 === 0;
const currentCell = { col: props.colIndex, row: props.rowIndex };
const isLastMove = compareCells(currentCell, lastMove);
const validMovePredicate = (start) => (move) => {
const startMove = move.path.at(0);
const endMove = move.path.at(-1);
return compareCells(startMove, start) && compareCells(endMove, currentCell);
};
const [{ isOver, canDrop }, dropReference] = useDrop(
() => ({
accept: 'piece',
canDrop: (start) => board.moves.some(validMovePredicate(start)),
collect: (monitor) => ({
canDrop: !!monitor.canDrop(),
isOver: !!monitor.isOver(),
}),
drop: (start) => {
doMove(board.moves.find(validMovePredicate(start)));
},
}),
[board.moves, doMove]
);
let bg = 'gray.300';
if (canDrop) {
bg = 'lightblue';
} else if ((canDrop && isOver) || isLastMove) {
bg = 'lightgreen';
} else if (isDark) {
bg = 'gray.500';
}
return (
<Square
ref={dropReference}
pos="relative"
h="100%"
p="0.2em"
bg={bg}
userSelect="none"
>
<Box
pos="absolute"
top="0.1rem"
right="0.1rem"
fontSize="0.4rem"
opacity={0.4}
userSelect="none"
>
{props.rowIndex}, {props.colIndex}
</Box>
{props.piece !== Pieces.NONE && (
<DraughtsPiece
piece={props.piece}
rowIndex={props.rowIndex}
colIndex={props.colIndex}
/>
)}
</Square>
);
}

View File

@ -0,0 +1,13 @@
const { Icon } = require('@chakra-ui/react');
export function DraughtsCrown(props) {
return (
<Icon viewBox="0 0 230 200" {...props}>
<path
d="m9,51 72,21 37-64 37,64 72-21-33,99H42
m75-74a15,29 0 1,0 2,0m71,86-11,33H57l-11-33"
fill="gold"
/>
</Icon>
);
}

View File

@ -0,0 +1,82 @@
import { useEffect } from 'react';
import {
Modal,
Button,
Text,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
useDisclosure,
Divider,
} from '@chakra-ui/react';
import { useDraughtsBoard } from '../DraughtsBoardContext';
import { useDraughtsWinner } from '../hooks/use-draughts-winner';
import { useDraughtsGame } from '../../game/DraughtsGameContext';
import { useDraughtsSettings } from '../../settings/DraughtsSettingsContext';
import { formatPlayer, Players, GameStates } from '@draughts/core';
function capitalizeFirstLetter(string) {
return string.at(0).toUpperCase() + string.slice(1);
}
function formatGameOverMessage(winner) {
if (winner === Players.NONE) return 'The game was a draw.';
// const winnerFormatted = capitalizeFirstLetter(formatPlayer(winner));
// return `${winnerFormatted} won the game.`;
}
export function DraughtsGameOverModal() {
const { isOpen, onOpen, onClose } = useDisclosure();
const { restartGame } = useDraughtsGame();
const { board } = useDraughtsBoard();
const { settingsModal } = useDraughtsSettings();
const { userWon, winner } = useDraughtsWinner();
useEffect(() => {
if (board.state === GameStates.PLAYING) return;
onOpen();
return onClose;
}, [board.state, onOpen, onClose]);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{userWon ? 'Wooo! Anda Menang. 🎉' : 'Semoga lain kali lebih beruntung... 💪'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text>{formatGameOverMessage(winner)}</Text>
<Divider marginY={5} />
<Text>
Mengapa bukan kesulitan komputer yang berbeda, Main Lagi Bosku, Anda Masih Kuat, Bertenaga?
</Text>
</ModalBody>
<ModalFooter>
<Button
mr={3}
colorScheme="blue"
onClick={() => {
onClose();
settingsModal.onOpen();
}}
>
Setting Game
</Button>
<Button
onClick={() => {
restartGame();
onClose();
}}
>
Main Lagi
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}

View File

@ -0,0 +1,64 @@
import { useDrag } from 'react-dnd';
import { Center } from '@chakra-ui/react';
import PropTypes from 'prop-types';
import { useDraughtsBoard } from '../DraughtsBoardContext';
import { useDraughtsSettings } from '../../settings/DraughtsSettingsContext';
import { DraughtsCrown } from './DraughtsCrown';
import {
compareCells,
pieceIsPlayer,
pieceIsQueen,
Pieces,
Players,
} from '@draughts/core';
export function DraughtsPiece(props) {
const { board } = useDraughtsBoard();
const { userPlayer } = useDraughtsSettings();
const isWhite = pieceIsPlayer(props.piece, Players.WHITE);
const activePlayer =
pieceIsPlayer(props.piece, userPlayer) &&
pieceIsPlayer(props.piece, board.playerToMove);
const currentCell = { col: props.colIndex, row: props.rowIndex };
const canDrag =
activePlayer &&
board.moves.some((move) => compareCells(move.path.at(0), currentCell));
const [, dragRef] = useDrag(
() => ({
canDrag: () => canDrag,
isDragging: (monitor) => compareCells(monitor.getItem(), currentCell),
item: currentCell,
type: 'piece',
}),
[board, canDrag, currentCell]
);
return (
<Center
ref={dragRef}
zIndex="1"
w="100%"
h="100%"
opacity={canDrag ? 1 : 0.8}
borderWidth="0.2em"
borderStyle="solid"
borderColor={isWhite ? 'gray.400' : 'gray.600'}
borderRadius="50%"
bgColor={isWhite ? 'yellow.50' : 'gray.900'}
>
{pieceIsQueen(props.piece) && (
<DraughtsCrown w="70%" h="70%" opacity="0.6" userSelect="none" />
)}
</Center>
);
}
DraughtsPiece.propTypes = {
colIndex: PropTypes.number,
piece: PropTypes.oneOf(Object.values(Pieces)).isRequired,
rowIndex: PropTypes.number,
};

View File

@ -0,0 +1,37 @@
import { createContext, useCallback, useContext } from 'react';
import PropTypes from 'prop-types';
import { useDraughtsBoard } from '../board/DraughtsBoardContext';
import { useDraughtsTimer } from './timer/use-draughts-timer';
import { useDraughtsComputer } from './computer/use-draughts-computer';
import { Players } from '@draughts/core';
export const DraughtsGameContext = createContext();
export const useDraughtsGame = () => useContext(DraughtsGameContext);
export function DraughtsGameProvider(props) {
const { resetBoard } = useDraughtsBoard();
const [whiteTimer, resetWhiteTimer] = useDraughtsTimer(Players.WHITE);
const [blackTimer, resetBlackTimer] = useDraughtsTimer(Players.BLACK);
useDraughtsComputer();
const restartGame = useCallback(() => {
resetBoard();
resetWhiteTimer();
resetBlackTimer();
}, [resetBoard, resetWhiteTimer, resetBlackTimer]);
return (
<DraughtsGameContext.Provider
value={{ blackTimer, restartGame, whiteTimer }}
>
{props.children}
</DraughtsGameContext.Provider>
);
}
DraughtsGameProvider.propTypes = {
children: PropTypes.node.isRequired,
};

View File

@ -0,0 +1,31 @@
import { useEffect, useRef } from 'react';
import { useDraughtsSettings } from '../../settings/DraughtsSettingsContext';
import { useDraughtsBoard } from '../../board/DraughtsBoardContext';
import { GameStates } from '@draughts/core';
export function useDraughtsComputer() {
const { board, doMove } = useDraughtsBoard();
const { userPlayer, computerDifficulty } = useDraughtsSettings();
const workerRef = useRef();
useEffect(() => {
if (board.state !== GameStates.PLAYING) return;
if (board.playerToMove === userPlayer) return;
workerRef.current = new Worker(new URL('worker.js', import.meta.url));
const handleWorkerMessage = (event) => {
doMove(event.data);
};
workerRef.current.addEventListener('message', handleWorkerMessage);
workerRef.current.postMessage({ board, computerDifficulty });
return () => {
workerRef.current.removeEventListener('message', handleWorkerMessage);
workerRef.current.terminate();
};
}, [board, userPlayer, computerDifficulty, doMove]);
return null;
}

View File

@ -0,0 +1,40 @@
import { ComputerDifficulty } from '../../settings/constants/computer-difficulty';
import { alphaBetaMove } from '@draughts/computer';
import { Board } from '@draughts/core';
const getRandomMove = (board) => {
return board.moves.at(Math.floor(Math.random() * board.moves.length));
};
const getComputerMove = (board, difficulty) => {
if (difficulty === ComputerDifficulty.MEDIUM) {
return alphaBetaMove(board, 3);
}
if (difficulty === ComputerDifficulty.HARD) {
return alphaBetaMove(board, 6);
}
return getRandomMove(board);
};
const getComputerTimeout = (board) => {
return 200 + board.moves.length * Math.floor(Math.random() * 200);
};
addEventListener('message', (event) => {
const startTime = Date.now();
const board = new Board(
event.data.board.position,
event.data.board.playerToMove,
event.data.board.firstMove
);
const move = getComputerMove(board, event.data.computerDifficulty);
const endTime = Date.now();
const elapsedTime = startTime - endTime;
const waitTime = Math.max(0, getComputerTimeout(board) - elapsedTime);
setTimeout(() => {
postMessage(move);
}, waitTime);
});

View File

@ -0,0 +1,43 @@
import { useState, useEffect, useMemo } from 'react';
import { useDraughtsBoard } from '../../board/DraughtsBoardContext';
import { GameStates } from '@draughts/core';
const TIMER_TICK = 100;
const INITIAL_TIME = 5 * 60 * 1000;
export function useDraughtsTimer(player) {
const { board } = useDraughtsBoard();
const [timer, setTimer] = useState(INITIAL_TIME);
useEffect(() => {
if (board.state !== GameStates.PLAYING) return;
if (board.firstMove) return;
if (board.playerToMove !== player) return;
const interval = setInterval(() => {
setTimer((timer) => {
const updatedTime = timer - TIMER_TICK;
return updatedTime >= 0 ? updatedTime : 0;
});
}, TIMER_TICK);
return () => {
clearInterval(interval);
setTimer((timer) => timer + 1000);
};
}, [player, board, setTimer]);
const timerInfo = useMemo(() => {
const totalSeconds = Math.floor(timer / 1000);
return {
complete: timer === 0,
millis: timer % 1000,
minutes: Math.floor(totalSeconds / 60),
seconds: totalSeconds % 60,
};
}, [timer]);
const resetTimer = () => setTimer(INITIAL_TIME);
return [timerInfo, resetTimer];
}

View File

@ -0,0 +1,41 @@
import { createContext, useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useDisclosure } from '@chakra-ui/react';
import { ComputerDifficulty } from './constants/computer-difficulty';
import { Players } from '@draughts/core';
export const DraughtsSettingsContext = createContext();
export const useDraughtsSettings = () => useContext(DraughtsSettingsContext);
export function DraughtsSettingsProvider(props) {
const settingsModal = useDisclosure();
const [userPlayer, setUserPlayer] = useState(props.userPlayer);
const [computerDifficulty, setComputerDifficulty] = useState(
props.computerDifficulty
);
const updateSettings = (newSettings) => {
setUserPlayer(newSettings.userPlayer);
setComputerDifficulty(newSettings.computerDifficulty);
};
return (
<DraughtsSettingsContext.Provider
value={{ computerDifficulty, settingsModal, updateSettings, userPlayer }}
>
{props.children}
</DraughtsSettingsContext.Provider>
);
}
export const DraughtsSettingsProviderProps = {
computerDifficulty: PropTypes.oneOf(Object.values(ComputerDifficulty)),
userPlayer: PropTypes.oneOf(Object.values(Players)),
};
DraughtsSettingsProvider.propTypes = {
children: PropTypes.node.isRequired,
...DraughtsSettingsProviderProps,
};

View File

@ -0,0 +1,5 @@
export const ComputerDifficulty = {
EASY: 'e',
HARD: 'h',
MEDIUM: 'm',
};

View File

@ -0,0 +1,30 @@
import { SettingsIcon, RepeatIcon } from '@chakra-ui/icons';
import { Button, VStack } from '@chakra-ui/react';
import { useDraughtsGame } from '../../game/DraughtsGameContext';
import { useDraughtsSettings } from '../DraughtsSettingsContext';
import { DraughtsSettingsModal } from './DraughtsSettingsModal';
export function DraughtsMenuView() {
const { restartGame } = useDraughtsGame();
const { settingsModal } = useDraughtsSettings();
return (
<VStack>
<DraughtsSettingsModal />
<Button
onClick={() => settingsModal.onOpen()}
rightIcon={<SettingsIcon />}
size="xs"
>
Buka Setting
</Button>
<Button
onClick={() => restartGame()}
rightIcon={<RepeatIcon />}
size="xs"
>
Mulai Lagi
</Button>
</VStack>
);
}

View File

@ -0,0 +1,94 @@
import { useState } from 'react';
import {
Modal,
Button,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
FormControl,
FormLabel,
Radio,
RadioGroup,
HStack,
Select,
Divider,
} from '@chakra-ui/react';
import { useDraughtsGame } from '../../game/DraughtsGameContext';
import { useDraughtsSettings } from '../DraughtsSettingsContext';
import { ComputerDifficulty } from '../constants/computer-difficulty';
import { Players } from '@draughts/core';
export function DraughtsSettingsModal() {
const { restartGame } = useDraughtsGame();
const {
userPlayer,
updateSettings,
computerDifficulty,
settingsModal: { isOpen, onClose },
} = useDraughtsSettings();
const [computerDifficultySelection, setComputerDifficultySelection] =
useState(computerDifficulty);
const [userPlayerSelection, setUserPlayerSelection] = useState(userPlayer);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Settings</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl as="fieldset">
<FormLabel as="legend">Pilih Warna</FormLabel>
<RadioGroup
onChange={setUserPlayerSelection}
value={userPlayerSelection}
>
<HStack>
<Radio value={`${Players.WHITE}`}>Putih</Radio>
<Radio value={`${Players.BLACK}`}>Hitam</Radio>
</HStack>
</RadioGroup>
</FormControl>
<Divider marginY={5} />
<FormControl>
<FormLabel htmlFor="computerDifficulty">
Computer difficulty
</FormLabel>
<Select
id="computerDifficulty"
onChange={(event) => {
setComputerDifficultySelection(event.target.value);
}}
value={computerDifficultySelection}
>
<option value={ComputerDifficulty.EASY}>Easy</option>
<option value={ComputerDifficulty.MEDIUM}>Medium</option>
<option value={ComputerDifficulty.HARD}>Hard</option>
</Select>
</FormControl>
</ModalBody>
<ModalFooter>
<Button mr={3} colorScheme="blue" onClick={onClose}>
Kembali Ke Permainan
</Button>
<Button
onClick={() => {
restartGame();
updateSettings({
computerDifficulty: computerDifficultySelection,
userPlayer: userPlayerSelection,
});
onClose();
}}
>
Mulai Permainan Baru
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}

View File

@ -0,0 +1,12 @@
import { HStack } from '@chakra-ui/react';
import { Logo } from './Logo';
import { Navigation } from './Navigation';
export function Header() {
return (
<HStack justify="space-between">
<Logo />
<Navigation />
</HStack>
);
}

View File

@ -0,0 +1,20 @@
import NextLink from 'next/link';
import { Heading, HStack } from '@chakra-ui/react';
import { DraughtsCrown } from '../draughts/board/views/DraughtsCrown';
export function Logo() {
return (
<NextLink href="/" passHref>
<HStack as="a">
<DraughtsCrown
bg="black"
padding={1}
borderRadius="50%"
height="1.5em"
width="1.5em"
/>
<Heading fontSize="2xl">Givan Checkers</Heading>
</HStack>
</NextLink>
);
}

View File

@ -0,0 +1,77 @@
import { useRef } from 'react';
import {
HStack,
VStack,
IconButton,
Link,
useDisclosure,
Drawer,
DrawerBody,
DrawerOverlay,
DrawerContent,
DrawerCloseButton,
DrawerHeader,
} from '@chakra-ui/react';
import { HamburgerIcon } from '@chakra-ui/icons';
const pages = [
{'url':'rules','name' : 'Peraturan'},
{'url':'strategies','name': 'Strategi'},
{'url':'history','name':'Sejarah'}
];
export function Navigation() {
const { isOpen, onOpen, onClose } = useDisclosure();
const btnRef = useRef();
return (
<>
<HStack as="nav" display={['none', 'none', 'block']} spacing={[2, 2, 4]}>
{pages.map((page) => (
<Link
key={`navlink-desktop-${page.url}`}
fontSize="lg"
fontWeight="bold"
href={`/${page.url}`}
>
{page.name}
</Link>
))}
</HStack>
<IconButton
ref={btnRef}
display={['block', 'block', 'none']}
aria-label="Open Menu"
icon={<HamburgerIcon />}
onClick={onOpen}
size="sm"
/>
<Drawer
finalFocusRef={btnRef}
isOpen={isOpen}
onClose={onClose}
placement="right"
size="xs"
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Explore</DrawerHeader>
<DrawerBody>
<VStack as="nav">
{pages.map((page) => (
<Link
key={`navlink-mobile-${page}`}
fontSize="lg"
fontWeight="bold"
href={`/${page}`}
>
{page}
</Link>
))}
</VStack>
</DrawerBody>
</DrawerContent>
</Drawer>
</>
);
}

View File

@ -0,0 +1,66 @@
import { Grid, GridItem } from '@chakra-ui/react';
import PropTypes from 'prop-types';
import { Header } from '../header/Header';
import { LeftSidebarAdvert } from '../adverts/LeftSidebarAdvert';
import { RightSidebarAdvert } from '../adverts/RightSidebarAdvert';
import { TopBannerAdvert } from '../adverts/TopBannerAdvert';
import { BottomBannerAdvert } from '../adverts/BottomBannerAdvert';
export function MainLayout(props) {
return (
<Grid
as="main"
rowGap={[3, 4]}
columnGap={[3, 4, 5, 6]}
templateRows={['0 auto auto auto auto 0', '0 auto auto 0']}
templateColumns={[
'0 minmax(0, 1fr) 0',
'0 minmax(4em, 1fr) min(78vh, 78vw) 0',
'0 minmax(4em, 1fr) min(78vh, 78vw) minmax(4em, 1fr) 0',
]}
>
<GridItem colSpan={1} colStart={[2, 3, 3]} rowStart={2}>
<Header />
</GridItem>
<GridItem colSpan={1} colStart={[2, 3]} rowStart={[4, 3]}>
{props.children}
</GridItem>
<GridItem
display={['block', 'none']}
colSpan={2}
colStart={2}
rowStart={3}
>
<TopBannerAdvert />
</GridItem>
<GridItem
display={['block', 'none']}
colSpan={2}
colStart={2}
rowStart={6}
>
<BottomBannerAdvert />
</GridItem>
<GridItem
display={['none', 'block']}
colStart={2}
rowSpan={[2, 3, 4]}
rowStart={[3, 3, 2]}
>
<LeftSidebarAdvert />
</GridItem>
<GridItem
display={['none', 'none', 'block']}
colStart={5}
rowSpan={[2, 2, 4]}
rowStart={[3, 3, 2]}
>
<RightSidebarAdvert />
</GridItem>
</Grid>
);
}
MainLayout.propTypes = {
children: PropTypes.node,
};