React JS 中的两个固定导航栏

Two Fixed Navbars in React JS

我有一个导航栏,它已经固定在顶部,看起来像这样:

现在我想在此基础上再添加一个导航,它应该如下所示:

导航栏应固定在顶部,第一个导航栏的高度应略小于第二个。 我现在使用的代码是:

<Navbar variant="light" fixed="top" expand="lg" className="pt-4 pb-4">
    <Container>
        <Navbar.Brand href="/"><Image src={logo}></Image></Navbar.Brand>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />
        <Navbar.Collapse id="basic-navbar-nav">
            <Nav className="mr-auto">
                <Nav.Link as={Link} to="/" className="ml-xl-4">Home</Nav.Link>
                <Nav.Link className="ml-xl-4">Shop</Nav.Link>
                <Nav.Link className="ml-xl-4">Sale</Nav.Link>
            </Nav>
            <Form inline>
                <FormControl type="text" placeholder="Search" className="mr-sm-5" />
            </Form>
            <Nav.Link as={Link} to="/"><Image src={accountIcon}></Image></Nav.Link>
            <Nav.Link as={Link} to="/cart" className="pr-0"><Image src={cartIcon}></Image></Nav.Link>
        </Navbar.Collapse>
    </Container>
</Navbar>

我正在使用 FontAwesome、StyledIcons、StyledComponents 和 ReactBootstrap。现在我无法在现有导航栏的顶部添加另一个导航栏。我试图在容器内创建另一个 Nav,但它失败了。请指导我对齐两个导航栏。

我离开 UI 库的一个原因是它们在外观自定义方面可能会限制您可以做的事情。我经常发现外观越定制化,我写的 CSS/Style 覆盖就越多。某些组件将具有完全不同的布局和相同组件的不同 类,这意味着更多 CSS 覆盖!因此,这种反复的斗争是我强烈推荐使用 CSS-in-JS 的强大功能的主要原因之一(例如 @emotion or styled-components)。

不过,这里有一个 工作演示 :

预览https://7m0hn.csb.app/

我使用 Typescript 而不是普通的 Javascript 因为 VSCode 允许您将鼠标悬停在组件上以在同一选项卡中查看 JSDoc description (you may need to scroll down to view the description within dialog). Holding ctrl, while mouse left-clicking on the component, opens a preview


另一种方法是手动将 className 添加到 ReactBootstrap 组件并应用 CSS 覆盖。在这种情况下,底栏将是:

position: fixed; // locks it to screen 
top: 30px; // at this position (factoring in the height of the top bar)

但同样,这不会像编写您自己的可重用组件那样灵活。您可能会发现自己创建了许多 类 来为不同的 pages/screen layouts/themes 设置相同组件的样式(例如 DOM versus this DOM; or heck, even compared to this DOM,它只是很少使用 UI 库)。


Fixed/index.ts

import styled from "@emotion/styled";

/**
 * The Fixed component is a custom styled component to mainly adjust related 'fixed: position" CSS properties.
 *
 * @param background - sets 'background' CSS property (default: "transparent").
 * @param bottom - sets 'bottom' CSS property (default: undefined).
 * @param height - sets 'height' CSS property (default: "auto").
 * @param left - sets 'left' CSS property (default: undefined).
 * @param padding - sets 'padding' CSS property (default: "0px").
 * @param right - sets 'right' CSS property (default: undefined).
 * @param top - sets 'top' CSS property (default: undefined).
 * @param width - sets 'width' CSS property (default: "100%").
 */
const Fixed = styled.div<{
  background?: string;
  bottom?: string;
  height?: string;
  left?: string;
  padding?: string;
  right?: string;
  top?: string;
  width?: string;
}>`
  position: fixed;
  display: flex;
  align-items: center;
  width: 100%;
  background: ${({ background }) => background || "transparent"};
  border-bottom: 2px solid #ccc;
  height: ${({ height }) => height || "auto"};
  padding: ${({ padding }) => padding || "0px"};
  top: ${({ top }) => top};
  right: ${({ right }) => right};
  bottom: ${({ bottom }) => bottom};
  left: ${({ left }) => left};
  width: ${({ width }) => width || "100%"};
`;

export default Fixed;

Flex/index.ts

import styled from "@emotion/styled";

/**
 * The Flex component is a custom styled component to mainly set 'flex' CSS properties.
 *
 * @param align - sets 'align-items' CSS property (default: "stretch").
 * @param justify - sets 'justify-content' CSS property (default: "flex-start").
 * @param padding - sets 'padding' CSS property (default: "0px").
 * @param width - sets 'width' CSS property (default: "100%").
 */
const Flex = styled.div<{
  align?:
    | "stretch"
    | "center"
    | "flex-start"
    | "flex-end"
    | "baseline"
    | "initial"
    | "inherit";
  justify?:
    | "flex-start"
    | "flex-end"
    | "center"
    | "space-between"
    | "space-around"
    | "space-evenly";
  padding?: string;
  width?: string;
}>`
  display: flex;
  justify-content: ${({ justify }) => justify || "flex-start"};
  align-items: ${({ align }) => align || "stretch"};
  padding: ${({ padding }) => padding || "0px"};
  width: ${({ width }) => width || "100%"};
`;

export default Flex;

Navbar/index.tsx

import * as React from "react";
import Nav from "react-bootstrap/Nav";
import NavDropdown from "react-bootstrap/NavDropdown";
import Navbar from "react-bootstrap/Navbar";
import Form from "react-bootstrap/Form";
import FormControl from "react-bootstrap/FormControl";
import { IoPersonOutline } from "react-icons/io5";
import { GiShoppingCart } from "react-icons/gi";
import Fixed from "../Fixed";
import Flex from "../Flex";

export type eventKey = string | null;

const TopNavBar = () => {
  const [language, setLanguage] = React.useState<eventKey>("DE");
  const [currency, SetCurrency] = React.useState<eventKey>("€ EUR");

  const handleLanguageChange = React.useCallback((eventKey: eventKey) => {
    setLanguage(eventKey);
  }, []);

  const handleCurrencyChange = React.useCallback((eventKey: eventKey) => {
    SetCurrency(eventKey);
  }, []);

  return (
    <>
      <Navbar className="navbar-top" fixed="top">
        <Flex justify="flex-end" padding="0 20px 0 0">
          <NavDropdown
            onSelect={handleLanguageChange}
            alignRight
            title={language}
            id="basic-nav-dropdown"
          >
            <NavDropdown.Item eventKey="EN">EN</NavDropdown.Item>
            <NavDropdown.Item eventKey="ES">ES</NavDropdown.Item>
            <NavDropdown.Item eventKey="DE">DE</NavDropdown.Item>
            <NavDropdown.Item eventKey="JP">JP</NavDropdown.Item>
          </NavDropdown>
          <NavDropdown
            onSelect={handleCurrencyChange}
            alignRight
            title={currency}
            id="basic-nav-dropdown"
          >
            <NavDropdown.Item eventKey="€ EUR">€ EUR</NavDropdown.Item>
            <NavDropdown.Item eventKey="£ GBP">£ GBP</NavDropdown.Item>
            <NavDropdown.Item eventKey="¥ JPY">¥ JPY</NavDropdown.Item>
            <NavDropdown.Item eventKey="$ USD">$ USD</NavDropdown.Item>
          </NavDropdown>
        </Flex>
      </Navbar>
      <Fixed background="#fff" padding="5px 60px 0 20px" top="30px">
        <Flex justify="flex-start">
          <Navbar.Brand href="/">Brand</Navbar.Brand>
        </Flex>
        <Flex justify="center">
          <Form inline>
            <FormControl
              type="text"
              placeholder="Search"
              className="search-box"
            />
          </Form>
        </Flex>
        <Flex justify="flex-end">
          <Nav.Link>
            <IoPersonOutline />
          </Nav.Link>
          <Nav.Link className="pr-0">
            <GiShoppingCart />
          </Nav.Link>
        </Flex>
      </Fixed>
    </>
  );
};

export default TopNavBar;

App.tsx

import Navbar from "./Navbar";
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles.css";

const App = () => (
  <div className="app">
    <Navbar />
    <h1>Hello CodeSandbox</h1>
    <h2>Start editing to see some magic happen!</h2>
    <p>
      Curabitur aliquet quam id dui posuere blandit. Nulla porttitor accumsan
      tincidunt. Nulla porttitor accumsan tincidunt. Curabitur arcu erat,
      accumsan id imperdiet et, porttitor at sem. Vestibulum ante ipsum primis
      in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit
      neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula.
      Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
      cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper
      sit amet ligula. Vestibulum ac diam sit amet quam vehicula elementum sed
      sit amet dui. Vestibulum ante ipsum primis in faucibus orci luctus et
      ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam
      vel, ullamcorper sit amet ligula. Cras ultricies ligula sed magna dictum
      porta. Pellentesque in ipsum id orci porta dapibus.
    </p>
    <p>
      Proin eget tortor risus. Donec sollicitudin molestie malesuada. Lorem
      ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum
      primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec
      velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula.
      Curabitur aliquet quam id dui posuere blandit. Curabitur aliquet quam id
      dui posuere blandit. Curabitur aliquet quam id dui posuere blandit.
      Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
      cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper
      sit amet ligula. Praesent sapien massa, convallis a pellentesque nec,
      egestas non nisi. Vestibulum ac diam sit amet quam vehicula elementum sed
      sit amet dui.
    </p>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget tortor
      risus. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus.
      Vivamus suscipit tortor eget felis porttitor volutpat. Pellentesque in
      ipsum id orci porta dapibus. Cras ultricies ligula sed magna dictum porta.
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque in
      ipsum id orci porta dapibus. Vestibulum ante ipsum primis in faucibus orci
      luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit
      amet aliquam vel, ullamcorper sit amet ligula. Praesent sapien massa,
      convallis a pellentesque nec, egestas non nisi.
    </p>
  </div>
);

export default App;

index.tsx

import { render } from "react-dom";
import App from "./App";

render(<App />, document.getElementById("root"));

styles.css

.app {
  margin-top: 120px;
  font-family: sans-serif;
  text-align: center;
  min-height: 120vh;
}

.navbar-top {
  padding: 0;
  background: #dadada;
  border-bottom: 2px solid #ccc;
}

.nav-link {
  padding: 2px 20px;
}

.search-box {
  height: 35px;
}

::-moz-focus-inner {
  border: 0;
}

::-webkit-scrollbar {
  display: none;
}

html,
body {
  margin: 0;
  padding: 0;
  background: #f9f9f9;
}

*,
:after,
:before {
  -ms-overflow-style: none;
  scrollbar-width: none;
  box-sizing: border-box;
}