Боковая навигация React.js — Учебник


Оглавление

  • Введение и предварительные условия
  • Базовая установка
  • Создание бургера
  • Создание бокового меню
  • Финальные штрихи

Введение и предварительные условия

Итак, для сегодняшнего урока я решил сделать кое-что на React. Если вы хотите следовать за мной, я рекомендую вам иметь некоторые знания о React, иначе некоторые вещи могут показаться вам непонятными. И, как всегда, будет YouTube-версия этого руководства, вы можете найти ее здесь 🙂

Вот что мы создаем сегодня:


Базовая установка

Итак, я решил сделать забавную вещь: у всех будет один и тот же сайт, но с разными цветовыми темами.
Вы откроете этот сайт. По сути, это блог, демонстрирующий 40 различных цветовых тем (я всегда использую этот сайт).

Теперь вам нужно открыть в Google генератор случайных чисел и покрутить его. Какое бы число вы ни получили, это и будет ваша цветовая тема. Вот что получилось у меня:


Запомните свою цветовую тему!

Теперь откройте папку, в которой будет находиться ваш код, запустите проект React и загрузите следующие пакеты:

npx create-react-app frontend
cd frontend
yarn add hamburger-react react-focus-lock react-icons styled-components
Войти в полноэкранный режим Выйти из полноэкранного режима

После этого откройте редактор кода и сохраните/создайте следующие файлы:

Быстрое примечание: Другие файлы self-created не понадобятся, поэтому смело delete их, также мы не будем использовать простой CSS, мы будем стилизовать наши компоненты с помощью styled-components.

Теперь измените код внутри App.js на:

import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";

function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyles />
      <div class="main-text">
        <h1>Hello World</h1>
      </div>
    </ThemeProvider>
  );
}

export default App;
Вход в полноэкранный режим Выйти из полноэкранного режима

И код внутри index.js на:

import React from "react";
import ReactDOM from "react-dom/client";

import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь внутри theme.js определите вашу тему:

export const theme = {
  primaryDark: "#2B3252",
  primaryLight: "#EF5455",
  primaryHover: "#FAD744",

  mobile: "576px",
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь вы можете использовать свою цветовую тему. Для primaryDark используйте более темный цвет в вашей теме. А для primaryLight используйте более светлый цвет. Если в вашей теме нет третьего цвета, используйте #FFFFFF или #000000 для primaryHover.

А теперь внутри global.js определите основные правила CSS:

import { createGlobalStyle } from "styled-components";

export const GlobalStyles = createGlobalStyle`
  html, body {
    margin: 0;
    padding: 0;
  }
  *, *::after, *::before {
    box-sizing: border-box;
  }
  body {
    display: flex;
    justify-content: center;
    align-items: center;

    background: ${({ theme }) => theme.primaryDark};
    color: ${({ theme }) => theme.primaryLight};

    height: 100vh;
    line-height: 1.6;

    font-family: "Roboto", sans-serif;
    font-size: 1.2rem;
    font-weight: normal;
  }
  h1 {
    font-size: 2rem;
    text-align: center;
    text-transform: uppercase;
  }
  div {
    text-align: center;
  }
  small {
    display: block;
  }
  a {
    color: ${({ theme }) => theme.primaryHover};
    text-decoration: none;
  }
  .main-text {
    margin-left: 20rem;
    margin-right: 20rem;
  }
`;
Вход в полноэкранный режим Выход из полноэкранного режима

Если у вас есть вопросы о том, как мы определяем CSS с помощью styled-components, задавайте их в комментариях!

Вот что у нас осталось:


Создание бургера

По адресу ./components/Burger создайте следующие файлы:

Внутри index.js напишите:

export { default } from "./Burger";
Войти в полноэкранный режим Выйти из полноэкранного режима

Внутри Burger.styled.js напишите:

import styled from "styled-components";

export const StyledBurger = styled.button`
  position: absolute;
  top: 5%;
  left: 2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  width: 2rem;
  height: 2rem;
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0;
  z-index: 10;
  span {
    width: 2rem;
    height: 0.25rem;
    background: ${({ theme, open }) =>
      open ? theme.primaryDark : theme.primaryLight};
    border-radius: 10px;
    transition: all 0.3s linear;
    position: relative;
    transform-origin: 1px;
    :first-child {
      transform: ${({ open }) => (open ? "rotate(45deg)" : "rotate(0)")};
    }
    :nth-child(2) {
      opacity: ${({ open }) => (open ? "0" : "1")};
      transform: ${({ open }) => (open ? "translateX(20px)" : "translateX(0)")};
    }
    :nth-child(3) {
      transform: ${({ open }) => (open ? "rotate(-45deg)" : "rotate(0)")};
    }
  }
`;
Войти в полноэкранный режим Выйти из полноэкранного режима

Внутри Burger.js мы будем использовать hamburger-react, который мы установили ранее. Это пакет, созданный специально для меню гамбургера. Вы можете узнать больше о нем на официальном сайте.
Теперь добавьте следующий код внутри Burger.js:

import React from "react";
import { bool, func } from "prop-types";

import { Twirl as Hamburger } from "hamburger-react";

import { StyledBurger } from "./Burger.styled";

const Burger = ({ open, setOpen, ...props }) => {
  const isExpanded = open ? true : false;

  return (
    <StyledBurger>
      <Hamburger
        toggled={open}
        toggle={setOpen}
        size={32}
        direction="right"
        duration={0.4}
        distance="lg"
        aria-expanded={isExpanded}
        color={open ? "#2B3252" : "#EF5455"}
      ></Hamburger>
    </StyledBurger>
  );
};

Burger.propTypes = {
  open: bool.isRequired,
  setOpen: func.isRequired,
};

export default Burger;
Вход в полноэкранный режим Выйти из полноэкранного режима

Быстрое замечание: в атрибуте color в <Hamburger> используйте эту формулу (так как мы используем разные темы):

Мы почти закончили! Внутри ./components/index.js добавьте:

export { default as Burger } from "./Burger";
Вход в полноэкранный режим Выйти из полноэкранного режима

И внутри App.js напишите:

import React, { useState } from "react";

import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";

import { Burger } from "./components";

function App() {
  const [open, setOpen] = useState(false);

  return (
    <ThemeProvider theme={theme}>
      <GlobalStyles />

      <Burger open={open} setOpen={setOpen} aria-controls={menuId} />

      <div class="main-text">
        <h1>Hello World!</h1>
      </div>
    </ThemeProvider>
  );
}

export default App;
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь, если вы запустите ваше приложение, вы увидите иконку гамбургера в левом верхнем углу 🙂


Создание бокового меню

Так же, как и Burger в папке Menu, создайте:

Внутри index.js добавьте:

export { default } from "./Menu";
Вход в полноэкранный режим Выйти из полноэкранного режима

Внутри Menu.styled.js добавить:

import styled from "styled-components";

export const StyledMenu = styled.nav`
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: ${({ theme }) => theme.primaryLight};
  transform: ${({ open }) => (open ? "translateX(0)" : "translateX(-100%)")};
  height: 100vh;
  text-align: left;
  padding: 2rem;
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 0.3s ease-in-out;
  @media (max-width: ${({ theme }) => theme.mobile}) {
    width: 100%;
  }
  a {
    font-size: 2rem;
    text-transform: uppercase;
    padding: 2rem 0;
    font-weight: bold;
    letter-spacing: 0.5rem;
    color: ${({ theme }) => theme.primaryDark};
    text-decoration: none;
    transition: color 0.6s linear;
    @media (max-width: ${({ theme }) => theme.mobile}) {
      font-size: 1.5rem;
      text-align: center;
    }
    &:hover {
      color: ${({ theme }) => theme.primaryHover};
    }
  }
  .icon {
    margin-right: 10px;
  }
`;
Войти в полноэкранный режим Выйти из полноэкранного режима

И, наконец, внутри Menu.js добавьте:

import React from "react";
import { bool } from "prop-types";

import { AiOutlineFileText } from "react-icons/ai";
import { RiPriceTag3Line, RiContactsBookLine } from "react-icons/ri";
import { StyledMenu } from "./Menu.styled";

const Menu = ({ open, ...props }) => {
  const isHidden = open ? true : false;
  const tabIndex = isHidden ? 0 : -1;

  return (
    <StyledMenu open={open} aria-hidden={!isHidden} {...props}>
      <a href="#" tabIndex={tabIndex}>
        <span aria-hidden="true">
          <AiOutlineFileText className="icon" />
        </span>
        About us
      </a>
      <a href="#" tabIndex={tabIndex}>
        <span aria-hidden="true">
          <RiPriceTag3Line className="icon" />
        </span>
        Pricing
      </a>
      <a href="#" tabIndex={tabIndex}>
        <span aria-hidden="true">
          <RiContactsBookLine className="icon" />
        </span>
        Contact
      </a>
    </StyledMenu>
  );
};

Menu.propTypes = {
  open: bool.isRequired,
};

export default Menu;
Войти в полноэкранный режим Выйти из полноэкранного режима

И не забудьте внутри ./components/index.js добавить:

...
export { default as Menu } from "./Menu";
Войти в полноэкранный режим Выйти из полноэкранного режима

И в App.js добавить:

import React, { useState, useRef } from "react";

import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";

import FocusLock from "react-focus-lock";
import { useOnClickOutside } from "./hooks";

import { Burger, Menu } from "./components";

function App() {
  const [open, setOpen] = useState(false);

  const node = useRef();
  const menuId = "main-menu";

  useOnClickOutside(node, () => setOpen(false));

  return (
    <ThemeProvider theme={theme}>
      <GlobalStyles />
      <Burger open={open} setOpen={setOpen} aria-controls={menuId} />
      <Menu open={open} setOpen={setOpen} id={menuId} />
      <div class="main-text">
        <h1>Hello World</h1>
      </div>
    </ThemeProvider>
  );
}

export default App;
Войти в полноэкранный режим Выйти из полноэкранного режима

И, мы закончили! Однако есть еще пара штрихов, которые мы могли бы добавить. Если вы не хотите этого делать, ничего страшного, боковая навигация прекрасно работает и так 🙂

Завершающие штрихи

Итак, я говорю о том, что если вы щелкните за пределами боковой навигации, она не закроется. Это нормально, если вы хотите, чтобы пользователь нажал на иконку X, чтобы закрыть его. Однако мне это не нравится 🙂

Внутри hooks.js (который мы создали в начале) добавьте:

import { useEffect } from "react";

export const useOnClickOutside = (ref, handler) => {
  useEffect(() => {
    const listener = (event) => {
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };
    document.addEventListener("mousedown", listener);

    return () => {
      document.removeEventListener("mousedown", listener);
    };
  }, [ref, handler]);
};
Войти в полноэкранный режим Выйти из полноэкранного режима

И измените код в App.js на:

import React, { useState, useRef } from "react";

import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";

import FocusLock from "react-focus-lock";
import { useOnClickOutside } from "./hooks";

import { Burger, Menu } from "./components";

function App() {
  const [open, setOpen] = useState(false);

  const node = useRef();
  const menuId = "main-menu";

  useOnClickOutside(node, () => setOpen(false));

  return (
    <ThemeProvider theme={theme}>
      <GlobalStyles />
      <div ref={node}>
        <FocusLock disabled={!open}>
          <Burger open={open} setOpen={setOpen} aria-controls={menuId} />
          <Menu open={open} setOpen={setOpen} id={menuId} />
        </FocusLock>
      </div>
      <div class="main-text">
        <h1>Hello World!</h1>
      </div>
    </ThemeProvider>
  );
}

export default App;
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, здесь мы использовали несколько вещей. Во-первых, мы использовали useRef, который является хуком React. Вы можете прочитать больше о useRef здесь. И мы использовали react-focus-lock. Вот официальная (я думаю) документация.

Теперь, мы закончили!

Ссылка на страницу github.

Надеюсь, вам понравилось. Если вам понравилось, не забудьте следовать за мной.
Ссылки на мои социальные сети:

24YoungMamba (Young Mamba) — GitHub

Я делаю сайты : )Я могу создать ваш сайт. 24YoungMamba имеет один доступный репозиторий. Следите за их кодом на GitHub.

github.com

Young Mamba — Medium

Читайте статьи от Young Mamba на Medium. Я делаю сайты👨💻. Каждый день Young Mamba и тысячи других авторов читают, пишут и делятся важными историями на Medium.

medium.com

Ошибка жидкости: внутренняя

tiktok.com
twitter.com

Оцените статью
devanswers.ru
Добавить комментарий