diff --git a/client/package.json b/client/package.json index 2af36d5..0732496 100644 --- a/client/package.json +++ b/client/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@headlessui/react": "^1.7.2", "@heroicons/react": "^2.0.11", "clsx": "^1.2.1", "react": "^18.2.0", diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index afa2c81..237de88 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -1,6 +1,7 @@ lockfileVersion: 5.4 specifiers: + '@headlessui/react': ^1.7.2 '@heroicons/react': ^2.0.11 '@types/react': ^18.0.17 '@types/react-dom': ^18.0.6 @@ -18,6 +19,7 @@ specifiers: vite: ^3.1.0 dependencies: + '@headlessui/react': 1.7.2_biqbaboplfbrettd7655fr4n2y '@heroicons/react': 2.0.11_react@18.2.0 clsx: 1.2.1 react: 18.2.0 @@ -323,6 +325,17 @@ packages: dev: true optional: true + /@headlessui/react/1.7.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-snLv2lxwsf2HNTOBNgHYdvoYZ3ChJE8QszPi1d/hl9js8KrFrUulTaQBfSyPbJP5BybVreWh9DxCgz9S0Z6hKQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /@heroicons/react/2.0.11_react@18.2.0: resolution: {integrity: sha512-bASjOgSSaYj8HqXWsOqaBiB6ZLalE/g90WYGgZ5lPm4KCCG7wPXntY4kzHf5NrLh6UBAcnPwvbiw1Ne9GYfJtw==} peerDependencies: diff --git a/client/src/assets/images/logo.png b/client/src/assets/images/logo.png new file mode 100644 index 0000000..9332bcb Binary files /dev/null and b/client/src/assets/images/logo.png differ diff --git a/client/src/components/FormField.tsx b/client/src/components/FormField.tsx new file mode 100644 index 0000000..8cb3671 --- /dev/null +++ b/client/src/components/FormField.tsx @@ -0,0 +1,22 @@ +import { ReactNode } from "react"; +import Input from "./Input"; + +interface FormFieldProps extends React.ComponentPropsWithoutRef<"input"> { + label: string; + bottomElement?: ReactNode; +} + +const FormField = ({ label, bottomElement, ...inputProps }: FormFieldProps) => { + return ( +
+ +
+ + {bottomElement} +
+ ); +}; + +export default FormField; diff --git a/client/src/components/InlineLink.tsx b/client/src/components/InlineLink.tsx new file mode 100644 index 0000000..6982714 --- /dev/null +++ b/client/src/components/InlineLink.tsx @@ -0,0 +1,38 @@ +import { FC } from "react"; +import clsx from "clsx"; +import { NavLink } from "react-router-dom"; + +export interface InlineLinkProps + extends React.ComponentPropsWithoutRef<"span"> { + to: string; + external?: boolean; +} + +const InlineLink: FC = ({ + to, + external, + className, + ...rest +}) => { + if (external === true) { + return ( + + + + ); + } + + return ( + + + + ); +}; + +export default InlineLink; diff --git a/client/src/components/LoginModal.tsx b/client/src/components/LoginModal.tsx new file mode 100644 index 0000000..f67b840 --- /dev/null +++ b/client/src/components/LoginModal.tsx @@ -0,0 +1,129 @@ +import React, { FC } from "react"; +import { Dialog } from "@headlessui/react"; +import { createPortal } from "react-dom"; +import { Tab } from "@headlessui/react"; +import clsx from "clsx"; +import FormField from "./FormField"; +import Button from "./Button"; +import InlineLink from "./InlineLink"; +import logo from "../assets/images/logo.png"; + +export interface LoginModelProps { + isOpen: boolean; + onClose: () => any; + defaultPage?: number; +} + +const LoginModal: FC = ({ defaultPage, isOpen, onClose }) => { + return createPortal( + +
+ +
+ + Log in to + twitch-clone + +
+ + + + {({ selected }) => ( + Log In + )} + + + {({ selected }) => ( + Sign Up + )} + + + + + + + Trouble logging in? + + } + /> + + + +

+ Creating an account allows you to participate in chat, follow + your favorite channels, and broadcast from your own channel. +

+ + + + +

+ By clicking Sign Up, you are agreeing to twitch-clone's{" "} + + Terms of Service + + . +

+ +
+
+
+
+
+
, + document.body + ); +}; + +interface LoginModalTabProps extends React.ComponentPropsWithoutRef<"p"> { + selected: boolean; +} + +const LoginModalTab: FC = ({ selected, ...rest }) => { + return ( +

+ ); +}; + +export default LoginModal; diff --git a/client/src/components/NavBar.tsx b/client/src/components/NavBar.tsx index 0cff89b..92ac784 100644 --- a/client/src/components/NavBar.tsx +++ b/client/src/components/NavBar.tsx @@ -1,16 +1,31 @@ import { TvIcon, UserIcon } from "@heroicons/react/24/outline"; -import { FC } from "react"; +import { FC, useState } from "react"; import Button from "./Button"; import Input from "./Input"; +import LoginModal from "./LoginModal"; +import logo from "../assets/images/logo.png"; const NavBar: FC = () => { + const [showLogin, setShowLogin] = useState(false); + const [showTab, setShowTab] = useState(0); + + const showLoginTab = () => { + setShowTab(0); + setShowLogin(true); + }; + + const showSignupTab = () => { + setShowTab(1); + setShowLogin(true); + }; + return (

); };