diff --git a/website/src/components/Navbar/MobileDrawer/_vars.scss b/website/src/components/Navbar/MobileDrawer/_vars.scss new file mode 100644 index 0000000..9065a5b --- /dev/null +++ b/website/src/components/Navbar/MobileDrawer/_vars.scss @@ -0,0 +1 @@ +$navbar-drawer-width: 80vw; \ No newline at end of file diff --git a/website/src/components/Navbar/MobileDrawer/index.tsx b/website/src/components/Navbar/MobileDrawer/index.tsx new file mode 100644 index 0000000..fd6266a --- /dev/null +++ b/website/src/components/Navbar/MobileDrawer/index.tsx @@ -0,0 +1,50 @@ +import c from "clsx" + +import s from "./styles.module.scss" + +import { Dispatch, ReactNode, SetStateAction, useState } from "react" + +interface MobileDrawerProps { + isOpen: boolean + setIsOpen: Dispatch> + secondaryMenu?: ReactNode + children: ReactNode +} + +const MobileDrawer = ({ isOpen, setIsOpen, secondaryMenu, children }: MobileDrawerProps) => { + const [menu, setMenu] = useState<'primary' | 'secondary'>(secondaryMenu ? 'secondary' : 'primary') + + return ( + <> +
{ setIsOpen(false); }} + /> +
+
+
+ + {children} +
+
+ + {secondaryMenu} +
+
+
+ + ) +} + +export default MobileDrawer \ No newline at end of file diff --git a/website/src/components/Navbar/MobileDrawer/styles.module.scss b/website/src/components/Navbar/MobileDrawer/styles.module.scss new file mode 100644 index 0000000..47daa06 --- /dev/null +++ b/website/src/components/Navbar/MobileDrawer/styles.module.scss @@ -0,0 +1,66 @@ +@use "./vars"; + +.backdrop { + position: fixed; + inset: 0; + visibility: hidden; + opacity: 0; + background-color: hsl(0 0% 0% / 0.1); + + &.backdropVisible { + visibility: visible; + opacity: 1; + } +} + +.drawer { + position: fixed; + top: 0; + bottom: 0; + left: 0; + width: vars.$navbar-drawer-width; + + transform: translate3d(-100%, 0, 0); + visibility: hidden; + opacity: 0; + + background-color: #fff; // TODO + box-shadow: 0px 8px 8px hsl(0 0 0 / 0.3); // TODO + + overflow-x: hidden; + + transition: opacity 400ms ease, + visibility 400ms ease, + transform 400ms ease; + + &.show { + transform: translateZ(0); + visibility: visible; + opacity: 1; + } + + .panes { + display: flex; + height: 100%; + + transition: transform 400ms ease; + + &.showSecondaryPane { + transform: translate3d(#{-1 * vars.$navbar-drawer-width}, 0, 0); + } + + .pane { + flex-shrink: 0; + padding: 1rem 0.5rem 0.5rem; + width: vars.$navbar-drawer-width; + overflow-x: hidden; + + .switchPaneButton { + width: 100%; + padding: 0.5rem 1.5rem; + background-color: hsl(0 0% 0% / 0.05); + margin-bottom: 0.5rem; + } + } + } +} \ No newline at end of file diff --git a/website/src/components/Navbar/MobileDrawer/styles.module.scss.d.ts b/website/src/components/Navbar/MobileDrawer/styles.module.scss.d.ts new file mode 100644 index 0000000..d4804c8 --- /dev/null +++ b/website/src/components/Navbar/MobileDrawer/styles.module.scss.d.ts @@ -0,0 +1,17 @@ +// AUTOGENERATED FILE -- DO NOT EDIT DIRECTLY +declare namespace StylesModuleScssNamespace { + export interface IStylesModuleScss { + backdrop: string; + backdropVisible: string; + drawer: string; + pane: string; + panes: string; + show: string; + showSecondaryPane: string; + switchPaneButton: string; + } +} + +declare const StylesModuleScssModule: StylesModuleScssNamespace.IStylesModuleScss; + +export = StylesModuleScssModule; diff --git a/website/src/components/Navbar/MobileMenuButton/_exports.module.scss b/website/src/components/Navbar/MobileMenuButton/_exports.module.scss new file mode 100644 index 0000000..9c0f915 --- /dev/null +++ b/website/src/components/Navbar/MobileMenuButton/_exports.module.scss @@ -0,0 +1,6 @@ +@use "@/styles/utils"; +@use "./vars" as *; + +:export { + iconSize: utils.strip-units($icon-size); +} \ No newline at end of file diff --git a/website/src/components/Navbar/MobileMenuButton/_exports.module.scss.d.ts b/website/src/components/Navbar/MobileMenuButton/_exports.module.scss.d.ts new file mode 100644 index 0000000..bf6ca85 --- /dev/null +++ b/website/src/components/Navbar/MobileMenuButton/_exports.module.scss.d.ts @@ -0,0 +1,10 @@ +// AUTOGENERATED FILE -- DO NOT EDIT DIRECTLY +declare namespace ExportsModuleScssNamespace { + export interface IExportsModuleScss { + iconSize: string; + } +} + +declare const ExportsModuleScssModule: ExportsModuleScssNamespace.IExportsModuleScss; + +export = ExportsModuleScssModule; diff --git a/website/src/components/Navbar/MobileMenuButton/_vars.scss b/website/src/components/Navbar/MobileMenuButton/_vars.scss new file mode 100644 index 0000000..1484471 --- /dev/null +++ b/website/src/components/Navbar/MobileMenuButton/_vars.scss @@ -0,0 +1 @@ +$icon-size: 32px; \ No newline at end of file diff --git a/website/src/components/Navbar/MobileMenuButton/index.tsx b/website/src/components/Navbar/MobileMenuButton/index.tsx new file mode 100644 index 0000000..542a4ce --- /dev/null +++ b/website/src/components/Navbar/MobileMenuButton/index.tsx @@ -0,0 +1,46 @@ +import c from "clsx"; +import type { MouseEventHandler } from "react"; + +import { iconSize as iconSizeString } from "./_exports.module.scss"; +import s from "./styles.module.scss" + + +interface MobileMenuButtonProps { + isOpen?: boolean + onClick: MouseEventHandler + className?: string +} + +const MobileMenuButton = ({ isOpen, onClick, className }: MobileMenuButtonProps) => { + return ( + + ) +} + +export default MobileMenuButton \ No newline at end of file diff --git a/website/src/components/Navbar/MobileMenuButton/styles.module.scss b/website/src/components/Navbar/MobileMenuButton/styles.module.scss new file mode 100644 index 0000000..923c5a2 --- /dev/null +++ b/website/src/components/Navbar/MobileMenuButton/styles.module.scss @@ -0,0 +1,35 @@ +@use "./vars"; + +.button { + width: vars.$icon-size; + height: vars.$icon-size; + cursor: pointer; + position: relative; + + .icon { + position: absolute; + top: 0; + left: 0; + + .hamburgerIcon, .closeIcon { + position: relative; + transition: all 0.15s ease; + width: vars.$icon-size; + height: vars.$icon-size; + } + + .closeIcon { + transform-origin: center center; + } + + .open { + transform: scale(1); + opacity: 1; + } + + .closed { + transform: scale(0); + opacity: 0; + } + } +} \ No newline at end of file diff --git a/website/src/components/Navbar/MobileMenuButton/styles.module.scss.d.ts b/website/src/components/Navbar/MobileMenuButton/styles.module.scss.d.ts new file mode 100644 index 0000000..77228c6 --- /dev/null +++ b/website/src/components/Navbar/MobileMenuButton/styles.module.scss.d.ts @@ -0,0 +1,15 @@ +// AUTOGENERATED FILE -- DO NOT EDIT DIRECTLY +declare namespace StylesModuleScssNamespace { + export interface IStylesModuleScss { + button: string; + closeIcon: string; + closed: string; + hamburgerIcon: string; + icon: string; + open: string; + } +} + +declare const StylesModuleScssModule: StylesModuleScssNamespace.IStylesModuleScss; + +export = StylesModuleScssModule; diff --git a/website/src/components/Navbar/index.tsx b/website/src/components/Navbar/index.tsx index 3222368..d869101 100644 --- a/website/src/components/Navbar/index.tsx +++ b/website/src/components/Navbar/index.tsx @@ -1,47 +1,101 @@ +import { useEffect, useState } from "react"; import Link from "next/link" -import c from "clsx"; import { useWindowSize } from "@/hooks/useWindowSize"; +import MobileSidebar from "@/components/Sidebar/Mobile"; +import MobileMenuButton from "./MobileMenuButton"; +import MobileDrawer from "./MobileDrawer"; import s from "./styles.module.scss" -const navLinks = [ - { href: '/workshops', label: 'Workshops' }, - { href: 'https://ai.acmucsd.com', label: 'Home' }, +import type { WindowSize } from "@/hooks/useWindowSize"; +import type { SidebarItem } from "@/lib/helpers/sidebar"; + +const navLinks: SidebarItem[] = [ + { type: 'doc', href: '/workshops', label: 'Workshops' }, + { type: 'doc', href: 'https://ai.acmucsd.com', label: 'Home' }, ] -const Navbar = (): JSX.Element => { +const useMobileMenu = (size: WindowSize) => { + const [isOpen, setIsOpen] = useState(false) + + useEffect(() => { + if (size !== 'mobile') { + setIsOpen(false); + } + }, [size]) + + return { isOpen, setIsOpen } +} + +interface NavbarProps { + sidebar?: SidebarItem[] + path: string +} +const Navbar = ({ sidebar, path }: NavbarProps): JSX.Element => { const size = useWindowSize() + const { isOpen, setIsOpen } = useMobileMenu(size); return ( ); } diff --git a/website/src/components/Navbar/styles.module.scss b/website/src/components/Navbar/styles.module.scss index 3bba9d0..61d6ac0 100644 --- a/website/src/components/Navbar/styles.module.scss +++ b/website/src/components/Navbar/styles.module.scss @@ -1,3 +1,4 @@ +@use "@/styles/mixins"; @use "@/styles/colors"; @use "./vars"; @@ -10,9 +11,13 @@ background-color: colors.$white; border-bottom: 1px solid #dddddd; padding: 0.5rem 2rem; - display: flex; - align-items: center; - justify-content: space-between; + + + .row { + display: flex; + align-items: center; + justify-content: space-between; + } .left { display: flex; @@ -33,14 +38,12 @@ } .right { - display: flex; - flex-wrap: nowrap; - align-items: center; - justify-content: center; - gap: 2rem; - - &.hidden { - display: none; + @include mixins.desktop-only { + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-content: center; + gap: 2rem; } .navItem { @@ -49,4 +52,15 @@ text-decoration: none; } } + + .mobileMenuButton { + @include mixins.mobile-only(); + } +} + +.mobileNavbarIcons { + margin: 0.5rem 0; + display: flex; + align-items: center; + justify-content: center; } \ No newline at end of file diff --git a/website/src/components/Navbar/styles.module.scss.d.ts b/website/src/components/Navbar/styles.module.scss.d.ts index 864771d..42dd17d 100644 --- a/website/src/components/Navbar/styles.module.scss.d.ts +++ b/website/src/components/Navbar/styles.module.scss.d.ts @@ -1,13 +1,14 @@ // AUTOGENERATED FILE -- DO NOT EDIT DIRECTLY - declare namespace StylesModuleScssNamespace { export interface IStylesModuleScss { - hidden: string; left: string; logo: string; + mobileMenuButton: string; + mobileNavbarIcons: string; navItem: string; navbar: string; right: string; + row: string; } } diff --git a/website/src/components/Sidebar/DesktopSidebar.tsx b/website/src/components/Sidebar/Desktop.tsx similarity index 89% rename from website/src/components/Sidebar/DesktopSidebar.tsx rename to website/src/components/Sidebar/Desktop.tsx index 3d1434c..e63b69b 100644 --- a/website/src/components/Sidebar/DesktopSidebar.tsx +++ b/website/src/components/Sidebar/Desktop.tsx @@ -16,8 +16,8 @@ import type { SidebarProps } from "."; const DesktopSidebar = ({ items, activePath }: SidebarProps): JSX.Element => { return ( -