Skip to content

Commit

Permalink
Merge pull request #176 from Jaewoook/feature/menu-bar-items
Browse files Browse the repository at this point in the history
Add menubar items
  • Loading branch information
Jaewoook authored Mar 1, 2024
2 parents 9db2cae + 59265b4 commit d6ef90f
Show file tree
Hide file tree
Showing 30 changed files with 391 additions and 141 deletions.
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nodejs 18.15.0
nodejs 18.19.0
2 changes: 1 addition & 1 deletion app/app.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ globalStyle("*, *:before, *:after", {
boxSizing: "border-box",
});

globalStyle("p", {
globalStyle("p, ul", {
margin: 0,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { useMemo, useState } from "react";
import { format } from "date-fns";

import { useClock } from "../../../hooks";
import { useClock } from "@/hooks";
import * as css from "./ClockIndicator.css";

export const ClockIndicator = () => {
Expand Down
4 changes: 2 additions & 2 deletions app/components/Layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
} from "react";
import type { ReactNode } from "react";

import { LayerContext } from "../contexts/LayerContext";
import type { ILayerContext } from "../contexts/LayerContext";
import { LayerContext } from "@/contexts/LayerContext";
import type { ILayerContext } from "@/contexts/LayerContext";

const BASE_Z_INDEX = 10;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useContext } from "react";
import { PiArrowCircleRightThin } from "react-icons/pi";

import { LockContext } from "@/contexts/LockContext";
import * as css from "./LoginProfile.css";


export const LoginProfile = () => {
const { unlock } = useContext(LockContext);

return (
<section className={css.userContainer}>
Expand All @@ -18,9 +20,9 @@ export const LoginProfile = () => {
/>
<div className={css.nameBox}>
<p className={css.nameText}>Jaewook Ahn</p>
<Link className={css.unlockText} href="/home">
<a className={css.unlockText} onClick={unlock}>
Click to unlock <PiArrowCircleRightThin className={css.unlockIcon} />
</Link>
</a>
</div>
</section>
);
Expand Down
42 changes: 42 additions & 0 deletions app/components/Menu/Menu.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { style } from "@vanilla-extract/css";

const MENU_BORDER_RADIUS = 8;
const MENU_ITEM_BORDER_RADIUS = 6;

export const menuIndicator = style({
display: "flex",
flexFlow: "row",
gap: 4,
color: "#fff",
fontSize: 14,
alignItems: "center",
position: "relative",
});

export const itemFrame = style({
position: "absolute",
top: "100%",
left: -8,
// TODO regularize z-index values
zIndex: 20,
});

export const itemWrapper = style({
color: "#fff",
boxShadow: "rgba(0, 0, 0, 0.55) 0px 10px 34px",
backgroundColor: "#333",
border: "1px solid #5c5c5c",
borderRadius: MENU_BORDER_RADIUS,
overflow: "hidden",
padding: "6px",
whiteSpace: "nowrap",
});

export const item = style({
padding: "4px 8px",
borderRadius: MENU_ITEM_BORDER_RADIUS,
userSelect: "none",
":hover": {
backgroundColor: "rgb(22 116 218)",
},
});
42 changes: 42 additions & 0 deletions app/components/Menu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";
import { useCallback, useContext } from "react";

import type { MenuItem } from "../../contexts/MenuBarContext";
import { menuBarContext } from "../../contexts/MenuBarContext";
import { useOutsideClick } from "../../hooks";
import * as css from "./Menu.css";

interface Props {
menuId: string;
name: string | React.ReactNode;
menuItems: MenuItem[];
}

export const Menu = (props: Props) => {
const { menuId, name, menuItems } = props;
const { openedMenuId, openMenu, closeMenu } = useContext(menuBarContext);
const ref = useOutsideClick<HTMLDivElement>(() => closeMenu());

const handleClick = useCallback(() => {
if (openedMenuId !== menuId) {
openMenu(menuId);
} else {
closeMenu();
}
}, [openedMenuId, menuId, closeMenu, openMenu]);

return (
<div className={css.menuIndicator} ref={ref}>
<p onClick={handleClick}>{name}</p>
<div className={css.itemFrame} style={{ visibility: openedMenuId === menuId ? "visible" : "hidden" }}>
<ul className={css.itemWrapper}>
{menuItems.map((menuItem) => (
<li key={`${menuItem.type}-${menuItem.name}`} className={css.item} onClick={menuItem.onClick}>
{menuItem.name}
</li>
))}
</ul>
</div>
</div>
);
};
13 changes: 5 additions & 8 deletions app/components/MenuBar/MenuBar.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@ export const container = style({
display: "flex",
});

export const menuWrapper = style({
export const wrapper = style({
display: "inline-flex",
flexFlow: "row",
alignItems: "center",
gap: 20,
flex: 1,
});

export const rightMenuWrapper = style([menuWrapper, {
flexDirection: "row-reverse",
export const rightMenuWrapper = style([wrapper, {
flexFlow: "row-reverse",
marginLeft: "auto",
}]);

export const clock = style({
color: "#fff",
fontSize: 14,
});
38 changes: 25 additions & 13 deletions app/components/MenuBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
"use client";
import { format } from "date-fns";
import { useMemo } from "react";
import { useCallback, useState } from "react";

import { useClock } from "../../hooks";
import { menuBarContext } from "../../contexts/MenuBarContext";
import type { IMenuBarContext } from "../../contexts/MenuBarContext";
import * as css from "./MenuBar.css";

export const MenuBar = () => {
const time = useClock();
const clock = useMemo(() => format(time, "E MMM d") + "\u00A0\u00A0" + format(time, "h:mm a"), [time]);
interface Props {
leftMenu: React.ReactNode;
rightMenu: React.ReactNode;
}

export const MenuBar = (props: Props) => {
const { leftMenu, rightMenu } = props;
const [openedMenuId, setOpenedMenuId] = useState<string | null>(null);

const openMenu = useCallback((menuId: string) => setOpenedMenuId(menuId), []);
const closeMenu = useCallback(() => setOpenedMenuId(null), []);
const value: IMenuBarContext = { openedMenuId, openMenu, closeMenu };

return (
<nav className={css.container}>
<div className={css.menuWrapper}>
</div>
<div className={css.rightMenuWrapper}>
<p className={css.clock}>{clock}</p>
</div>
</nav>
<menuBarContext.Provider value={value}>
<nav className={css.container}>
<div className={css.wrapper}>
{leftMenu}
</div>
<div className={css.rightMenuWrapper}>
{rightMenu}
</div>
</nav>
</menuBarContext.Provider>
);
};
7 changes: 7 additions & 0 deletions app/components/Menus/Menus.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { style } from "@vanilla-extract/css";

import { menuIndicator } from "../Menu/Menu.css";

export const batteryIndicator = style([menuIndicator, {
fontSize: 12,
}]);
78 changes: 78 additions & 0 deletions app/components/Menus/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";
import { format } from "date-fns";
import { useContext, useEffect, useMemo, useState } from "react";
import { IoBatteryCharging, IoBatteryFull, IoBatteryHalf, IoCodeSlash } from "react-icons/io5";

import { LockContext } from "@/contexts/LockContext";
import { useClock } from "@/hooks";

import { Menu } from "../Menu";
import { menuIndicator } from "../Menu/Menu.css";
import * as css from "./Menus.css";

export const PortfolioMenu = () => {
const { lock } = useContext(LockContext);
return (
<Menu
menuId="menu-portfolio"
name={<IoCodeSlash size={24} />}
menuItems={[
{
id: "about-this-site",
name: "About This Site",
type: "default",
checked: false,
disabled: false,
onClick: () => {},
},
{
id: "lock-screen",
name: "Lock Screen",
type: "default",
checked: false,
disabled: false,
onClick: lock,
}
]}
/>
);
};

export const ClockIndicator = () => {
const time = useClock();
const clock = useMemo(() => format(time, "E MMM d") + "\u00A0\u00A0" + format(time, "h:mm a"), [time]);

return <p className={menuIndicator}>{clock}</p>;
};

export const BatteryIndicator = () => {
const [battery, setBattery] = useState<Battery>({
charging: false,
chargingTime: 0,
dischargingTime: 0,
level: 1,
});
const batteryPercent = useMemo(() => (battery.level * 100).toString() + "%", [battery]);
const batteryIcon = useMemo(() => {
if (battery.charging) {
return <IoBatteryCharging size={22} />;
}

if (battery.level !== 1) {
return <IoBatteryHalf size={22} />;
}

return <IoBatteryFull size={22} />;
}, [battery]);

useEffect(() => {
window.navigator.getBattery?.().then((battery) => setBattery(battery));
}, []);

return (
<p className={css.batteryIndicator}>
{batteryPercent}
{batteryIcon}
</p>
);
};
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Image from "next/image";
import Link from "next/link";
import { PiGithubLogo, PiLinkedinLogo } from "react-icons/pi";

import { Window } from "../../../components/Window";
import { Window } from "../Window";
import * as css from "./Profile.css";

export const Profile = () => {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import axios from "axios";
import { PDFium } from "pdfium.js";
import { useCallback, useEffect, useRef, useState } from "react";

import { Window } from "../../../components/Window";
import { PDFRenderer } from "../../../modules/pdf-renderer";
import { Window } from "../Window";
import { PDFRenderer } from "../../modules/pdf-renderer";
import * as css from "./Resume.css";

export const Resume = () => {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PropsWithChildren } from "react";
import { Window } from "../../../components/Window";
import { Window } from "../Window";
import * as css from "./Settings.css";

interface RowProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import Image from "next/image";
import { useCallback, useContext } from "react";

import { Shortcut } from "../../components/Shortcut";
import { LayerContext } from "../../contexts/LayerContext";
import { LayerContext } from "../contexts/LayerContext";
import { Shortcut } from "./Shortcut";

export const ProfileShortcut = () => {
const { addLayer } = useContext(LayerContext);
Expand Down
5 changes: 3 additions & 2 deletions app/components/Window/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import { assignInlineVars } from "@vanilla-extract/dynamic";
import { useCallback, useState } from "react";

import { useDrag } from "../../hooks";
import type { DragEvent, DragEventHandler } from "../../hooks/useDrag";
import { useDrag } from "@/hooks";
import type { DragEvent, DragEventHandler } from "@/hooks/useDrag";

import { Layer } from "../Layer";
import * as css from "./Window.css";

Expand Down
32 changes: 32 additions & 0 deletions app/components/lock-screen/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use client";
import { assignInlineVars } from "@vanilla-extract/dynamic";
import { useContext, useEffect, useState } from "react";

import { LockContext } from "@/contexts/LockContext";
import { ClockIndicator } from "../ClockIndicator";
import { LoginProfile } from "../LoginProfile";
import { Wallpaper } from "../Wallpaper";
import * as css from "./lock-screen.css";

export const LockScreen = () => {
const { isLocked } = useContext(LockContext);
const [isVisible, setVisible] = useState(true);

useEffect(() => {
if (isLocked && !isVisible) {
setVisible(true);
}
}, [isLocked, isVisible]);

return (
<div
className={isLocked ? css.lockAnim : css.unlockAnim}
style={assignInlineVars({ [css.isVisible]: isVisible ? "visible" : "hidden" })}
onAnimationEnd={() => setVisible(false)}
>
<Wallpaper />
<ClockIndicator />
<LoginProfile />
</div>
);
};
Loading

0 comments on commit d6ef90f

Please sign in to comment.