Next js

[에어비엔비] #3 Navbar Dropdown + signup, login modal 구현

호박고구마123 2024. 4. 7. 13:26

오른쪽 Navbar 상단위에 유저 정보를 클릭시 로그인, 회원가입 드랍다운이 가능하도록 구현하였다.

Ant design + tailwindcss를 사용하였으며, 코드는 다음과 같다.

 

navbar.tsx

import styled from "@emotion/styled";
import { Modal } from "antd";
import { ReactNode } from "react";
import Line from "../../../common/liine/line";

interface BaseModalProps {
  children: ReactNode;
  isModalOpen: boolean;
  title: string;
  onCancel: () => void;
}

const AuthModal = styled(Modal)`
  .ant-modal-title {
    font-size: 15epx;
    text-align: center;
  }
`;

function Footer(): JSX.Element {
  return (
    <>
      <div className="flex justify-center">
        <button className="w-full h-12 text-white bg-pink-700 rounded-lg border-none">
          계속
        </button>
      </div>
    </>
  );
}

export default function AuthLayout({
  children,
  title,
  isModalOpen,
  onCancel,
}: BaseModalProps): JSX.Element {
  return (
    <>
      <AuthModal
        title={title}
        open={isModalOpen}
        centered={true}
        width={530}
        footer={Footer}
        onCancel={onCancel}
      >
        <Line />
        <h2 className="text-xl font-semibold mt-5">
          에어비엔비에 오신걸 환영합니다.
        </h2>
        {children}
      </AuthModal>
    </>
  );
}

 

user.tsx

 

import { useState } from "react";
import LoginModal from "../../units/modals/auth/login_modal";
import SignupModal from "../../units/modals/auth/signup_modal";
import Search from "./search";
import User from "./user";

export default function Navbar(): JSX.Element {
  const [loginModalOpend, setLoginModalOpend] = useState(false);
  const [signupModalOpend, setSignupModalOpend] = useState(false);
  const handleSignup = (): void => {
    setSignupModalOpend((prev) => !prev);
  };

  const handleLogin = (): void => {
    setLoginModalOpend((prev) => !prev);
  };

  return (
    <>
      <nav className="w-full h-40 bg-white font-semibold pt-4">
        <div className="flex justify-between items-center">
          <h2 className="text-red-500 text-2xl">airbnb</h2>
          <User handleSignup={handleSignup} handleLogin={handleLogin} />
        </div>
        <Search />
      </nav>
      {loginModalOpend ? (
        <LoginModal
          loginModalOpend={loginModalOpend}
          handleLogin={handleLogin}
        />
      ) : (
        ""
      )}
      {signupModalOpend ? (
        <SignupModal
          signupModalOpend={signupModalOpend}
          handleSignup={handleSignup}
        />
      ) : (
        ""
      )}
    </>
  );
}

 

 

실제 결과

 

 

 

<모달 구현>

 

layout.tsx

import { faBars } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
import { Dropdown, Space, Typography } from "antd";
import type { MenuProps } from "antd";

interface UserProps {
  handleSignup: () => void;
  handleLogin: () => void;
}

export default function User({
  handleSignup,
  handleLogin,
}: UserProps): JSX.Element {
  const items: MenuProps["items"] = [
    {
      key: "1",
      label: "로그인",
      style: { height: "50%", backgroundColor: "white", color: "black" },
      onClick: () => {
        handleLogin();
      },
    },
    {
      key: "2",
      label: "회원가입",
      style: {
        height: "50%",
        backgroundColor: "white",
        color: "black",
      },
      onClick: () => {
        handleSignup();
      },
    },
  ];

  return (
    <>
      <Dropdown
        menu={{
          items,
          selectable: true,
          defaultSelectedKeys: ["1"],
          style: { width: "220px", height: "100px" },
        }}
      >
        <Typography.Link>
          <Space>
            <button className="w-24 h-12 flex justify-between items-center border rounded-full pl-3 pr-3">
              <FontAwesomeIcon icon={faBars} />
              <image className="w-1/2 h-3/4 flex justify-center items-center bg-black rounded-full text-white text-sm">
                U
              </image>
            </button>
          </Space>
        </Typography.Link>
      </Dropdown>
    </>
  );
}

 

signup_modal.tsx

 

import { Input } from "antd";
import AuthLayout from "./layout";

interface SignupProps {
  signupModalOpend: boolean;
  handleSignup: () => void;
}

export default function SignupModal({
  signupModalOpend,
  handleSignup,
}: SignupProps): JSX.Element {
  return (
    <>
      <AuthLayout
        title="로그인 또는 회원가입"
        isModalOpen={signupModalOpend}
        onCancel={handleSignup}
      >
        <form className="space-y-3 mt-5">
          <Input placeholder="이메일" className="h-10" />
          <Input placeholder="이름" className="h-10" />
          <Input placeholder="휴대폰번호" className="h-10" />
          <Input.Password placeholder="패스워드" className="h-10" />
          <Input.Password placeholder="패스워드 확인" className="h-10" />
        </form>
      </AuthLayout>
    </>
  );
}

 

login_modal.tsx

import { Input } from "antd";
import AuthLayout from "./layout";

interface LoginProps {
  loginModalOpend: boolean;
  handleLogin: () => void;
}

export default function LoginModal({
  loginModalOpend,
  handleLogin,
}: LoginProps): JSX.Element {
  return (
    <>
      <AuthLayout
        title="로그인 또는 회원가입"
        isModalOpen={loginModalOpend}
        onCancel={handleLogin}
      >
        <form className="space-y-3 mt-4">
          <Input placeholder="이메일" className="h-10" />
          <Input.Password placeholder="패스워드" className="h-10" />
        </form>
      </AuthLayout>
    </>
  );
}

 

 

 

결과

회원가입 모달