В предыдущем посте, где я обсуждал, как нарисовать шахматную доску с помощью CSS flexbox, я упомянул, что хотел бы включить имена строк и столбцов в пользовательский интерфейс, и в идеале я сделал бы это с помощью CSS Макет сетки". Ну, я только что сделал это сегодня! Вы можете проверить последнюю версию моей шахматной доски на «https://css-chess-board.netlify.app/.

Ниже приведены 2 ключевых изменения, которые я внес в шахматную доску.

Добавление имен строк и столбцов с использованием макета сетки CSS

Этот коммит имеет все соответствующие изменения.

Сначала я создал .chess-board div сетку с 10 столбцами и 10 строками, используя приведенный ниже код CSS.

.chess-board {
    display: grid;
    grid-template: 
        repeat(10, $cell-size) / repeat(10, $cell-size)
    ;
}

Вы спросите, почему 10 строк и 10 столбцов? Потому что я хочу структурировать сетку следующим образом.

  | a  b  c  d  e  f  g  h  |
--------------------------------
8 | R  N  B  Q  K  B  N  R  |  8
7 | P  P  P  P  P  P  P  P  |  7
6 |                         |  6
5 |                         |  5
4 |                         |  4
3 |                         |  3
2 | P  P  P  P  P  P  P  P  |  2
1 | R  N  B  Q  K  B  N  R  |  1
--------------------------------
  | a  b  c  d  e  f  g  h  |

В приведенной выше сетке верхняя и нижняя строки будут использоваться для имен столбцов, а самые левые и самые правые столбцы — для имен строк. Это оставляет нам большую область 8x8 в центре сетки, где будет размещена существующая шахматная доска.

Чтобы расположить доску, я использовал свойство CSS grid-area ниже, чтобы указать, что существующая доска начинается со строки 2 и столбца 2 сетки и охватывает 8 строк и 8 столбцов.

grid-area: 2 / 2 / span 8 / span 8;

Чтобы расположить другие компоненты, я обновил код JSX ChessBoard следующим образом.

export const ChessBoard = () => {
    const board = [
        ['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖'],
        ['♙', '♙', '♙', '♙', '♙', '♙', '♙', '♙'],
        ['', '',  '',  '',  '',  '',  '',  ''],
        ['', '',  '',  '',  '',  '',  '',  ''],
        ['', '',  '',  '',  '',  '',  '',  ''],
        ['', '',  '',  '',  '',  '',  '',  ''],
        ['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'],
        ['♜', '♞', '♝', '♛','♚', '♝', '♞', '♜'],
    ]
    return (
        <div className="chess-board">
            <ColNames direction="top" />

            <RowNames gridArea="2 / 1 / span 8 / span 1" direction="left" />
            <Board board={board} />
            <RowNames gridArea="2 / 10 / span 8 / span 1" direction="right" />

            <ColNames gridArea="10 / 2 / span 1 / span 8" direction="bottom" />
        </div>
    );
}

Свойство gridArea используется внутри компонентов ColNames и RowNames для установки свойства grid-area.

const RowNames = ({gridArea, direction}) => {
    const names = [1, 2, 3, 4, 5, 6, 7, 8];
    return (
        <div className="row-names" style={{ gridArea }}>
            {
                names.map((name) => 
                    <div key={`${direction} ${name}`} className={`row-name ${direction}`}>
                        <strong>{name}</strong>
                    </div>
                )
            }
        </div>
    );
}

const ColNames = ({ gridArea, direction }) => {
    const names = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
    return (
        <div className="col-names" style={{ gridArea }}>
            {
                names.map((name) => 
                    <div key={`${direction} ${name}`} className={`col-name ${direction}`}>
                        <strong>{name}</strong>
                    </div>
                )
            }
        </div>
    );
}

Обратите внимание, что в более позднем коммите я изменил код, чтобы переместить все части, связанные с gridArea, в файл CSS, потому что я не всегда являюсь поклонником обработки стилей CSS в коде JavaScript.

Разрешение пользователям переходить на другую сторону с помощью контекстного API React

Шахматная доска имеет белые фигуры внизу и черные фигуры наверху, потому что она была построена с предположением, что сторона игрока белая. На самом деле игрок не всегда играет одним цветом, поэтому было бы лучше предоставить пользователям возможность переключаться на другую сторону.

Для реализации этой возможности я использую компонент Switch React Material UI library. Когда состояние переключателя меняется, нам нужно обновить пользовательский интерфейс следующим образом:

  1. Обратный порядок имен столбцов
  2. Обратный порядок имен строк
  3. Перевернуть строки содержимого шахматной доски
  4. Поменяйте местами короля и ферзя для обеих сторон так, чтобы белый ферзь начинал с белой клетки, а черный ферзь начинал с черной клетки.

Как я должен сообщить компонентам, что состояние переключателя изменилось? Наивный подход:

  1. Используйте useState для управления состоянием компонента Switch.
  2. Пузырьком поднимайте установщик состояния коммутатора вверх по DOM до самого нижнего общего предка коммутатора и шахматной доски.
  3. Передать состояние на доску как свойство в реквизите
  4. Продолжайте сверлить опору, если это необходимо

Хотя это определенно работает, реализовать это не очень весело, и это не приносит пользы нашему коду, потому что:

  1. Состояние переключателя должно быть передано через компоненты пользовательского интерфейса, которым на самом деле не нужно знать об этих данных.
  2. Потребуется дополнительная работа, если мы захотим позже изменить иерархию компонентов.

Это та ситуация, в которой Context API действительно помогает.

Сначала я создал контекст.

const PieceColorContext = createContext(undefined);

Из объекта контекста я определяю его поставщика контекста и настраиваемый хук

export const WHITE = "white";
export const BLACK = "black";

export const PieceColorProvider = ({ children }) => {
    const [pieceColor, setPieceColor] = useState(WHITE);

    const togglePieceColor = () => setPieceColor(pieceColor === WHITE ? BLACK : WHITE);

    return (
        <PieceColorContext.Provider value={{
                pieceColor,
                togglePieceColor,
            }}
            >
            {children}
        </PieceColorContext.Provider>
    );
};

export const usePieceColorContext = () => useContext(PieceColorContext);

Поставщику контекста PieceColorProvider необходимо обернуть компонент App, чтобы предоставляемый им контекст, то есть объект, передаваемый реквизиту value, был доступен для всех компонентов-потомков App.

import { PieceColorProvider } from './PieceColorContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <PieceColorProvider>
      <App />
    </PieceColorProvider>
  </React.StrictMode>
);

Как только это будет сделано, любой потомок App сможет использовать пользовательский хук usePieceColorContext для доступа к текущему значению и функции установки бокового переключателя, т. е. для доступа к предоставленному контексту. Ниже приведен обновленный код RowNames . Все соответствующие изменения были внесены в этот коммит.

export const RowNames = ({gridArea, direction}) => {
    const names = [1, 2, 3, 4, 5, 6, 7, 8];
    const { pieceColor } = usePieceColorContext();
    if (pieceColor === BLACK) {
        names.reverse();
    }
    return (
        <div className="row-names" style={{ gridArea }}>
            {
                names.map((name) => 
                    <div key={`${direction} ${name}`} className={`row-name ${direction}`}>
                        <strong>{name}</strong>
                    </div>
                )
            }
        </div>
    );
}

Наслаждаться!

Хотя я не планирую создавать полную и играбельную шахматную игру, я не хочу, чтобы этот проект останавливался на достигнутом. Ниже приведены несколько идей, которые у меня есть на данный момент:

  1. Загрузите головоломку дня с chess.com и покажите ее на доске.
  2. Загрузите сценарий шахматной партии и разрешите людям просматривать игру
  3. 🤔