Skip to content

Commit

Permalink
Perf layout userMenu component
Browse files Browse the repository at this point in the history
  • Loading branch information
slhmy committed Sep 26, 2023
1 parent 7e16f16 commit bf677ea
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 124 deletions.
3 changes: 3 additions & 0 deletions src/i18n/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const EN_US_TRANSLATIONS: Resource = {
Problem: "Problem",
User: "User",
Contest: "Contest",
"Admin page": "Admin page",
"Main page": "Main page",
"Sign out": "Sign out",
},
};

Expand Down
3 changes: 3 additions & 0 deletions src/i18n/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const ZH_CN_TRANSLATIONS: Resource = {
Problem: "问题",
User: "用户",
Contest: "竞赛",
"Admin page": "管理页",
"Main page": "首页",
"Sign out": "登出",
},
};

Expand Down
81 changes: 81 additions & 0 deletions src/layouts/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Menu, Transition } from "@headlessui/react";
import { Fragment } from "react";
import { ChevronDownIcon } from "@heroicons/react/20/solid";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { joinClasses } from "../utils/common";

export interface UserMenuProps {
avatarUrl?: string;
userName?: string;
navigation?: Array<{ name: string; href: string }>;
}

/**
* @param {string} props.avatarUrl
* @param {string} props.userName
* @param {Array<{ name: string, href: string }>} props.navigation The name of navigation will be translated.
*/
const UserMenu: React.FC<UserMenuProps> = (props) => {
const navigate = useNavigate();
const { t } = useTranslation();

return (
<>
{/* Profile dropdown */}
<Menu as="div" className="relative">
<Menu.Button className="-m-1.5 flex items-center p-1.5">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full bg-gray-50"
src={props.avatarUrl}
alt=""
/>
<span className="hidden lg:flex lg:items-center">
<span
className="ml-4 text-sm font-semibold leading-6 text-gray-900"
aria-hidden="true"
>
{props.userName}
</span>
<ChevronDownIcon
className="ml-2 h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{props.navigation?.map((item) => (
<Menu.Item key={item.name}>
{({ active }) => (
<div
className={joinClasses(
active ? "bg-gray-100" : "",
"block cursor-pointer px-4 py-2 text-sm text-gray-700",
)}
onClick={() => {
navigate(item.href);
}}
>
{t(item.name)}
</div>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</Menu>
</>
);
};

export default UserMenu;
77 changes: 13 additions & 64 deletions src/layouts/adminLayout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Fragment, useState } from "react";
import { Dialog, Menu, Transition } from "@headlessui/react";
import { Dialog, Transition } from "@headlessui/react";
import {
Bars3Icon,
HomeIcon,
UsersIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import { ChevronDownIcon } from "@heroicons/react/20/solid";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { joinClasses } from "../../utils/common";
import UserMenu from "../UserMenu";

const navigation = [
{ name: "Problem", href: "/admin/problem", icon: HomeIcon, current: true },
Expand All @@ -20,21 +20,16 @@ const user = {
imageUrl: "/avatars/male-avatar-1.svg",
};
const userNavigation = [
{ name: "Main Page", href: "/problem" },
{ name: "Main page", href: "/problem" },
{ name: "Sign out", href: "#" },
];

function classNames(...classes: any) {
return classes.filter(Boolean).join(" ");
}

interface SidebarProps {
children: JSX.Element;
}

export default function Sidebar(props: SidebarProps) {
const [sidebarOpen, setSidebarOpen] = useState(false);
const navigate = useNavigate();
const { t } = useTranslation();

return (
Expand Down Expand Up @@ -111,7 +106,7 @@ export default function Sidebar(props: SidebarProps) {
{navigation.map((item) => (
<li key={item.name}>
<div
className={classNames(
className={joinClasses(
item.current
? "bg-indigo-700 text-white"
: "text-indigo-200 hover:bg-indigo-700 hover:text-white",
Expand All @@ -120,7 +115,7 @@ export default function Sidebar(props: SidebarProps) {
onClick={() => {}}
>
<item.icon
className={classNames(
className={joinClasses(
item.current
? "text-white"
: "text-indigo-200 group-hover:text-white",
Expand Down Expand Up @@ -163,15 +158,15 @@ export default function Sidebar(props: SidebarProps) {
<li key={item.name}>
<a
href={item.href}
className={classNames(
className={joinClasses(
item.current
? "bg-indigo-700 text-white"
: "text-indigo-200 hover:bg-indigo-700 hover:text-white",
"group flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6",
)}
>
<item.icon
className={classNames(
className={joinClasses(
item.current
? "text-white"
: "text-indigo-200 group-hover:text-white",
Expand Down Expand Up @@ -206,57 +201,11 @@ export default function Sidebar(props: SidebarProps) {
<div className="flex flex-1 justify-end gap-x-4 self-stretch lg:gap-x-6">
<div className="flex items-center gap-x-4 lg:gap-x-6">
{/* Profile dropdown */}
<Menu as="div" className="relative">
<Menu.Button className="-m-1.5 flex items-center p-1.5">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full bg-gray-50"
src={user.imageUrl}
alt=""
/>
<span className="hidden lg:flex lg:items-center">
<span
className="ml-4 text-sm font-semibold leading-6 text-gray-900"
aria-hidden="true"
>
{user.name}
</span>
<ChevronDownIcon
className="ml-2 h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{userNavigation.map((item) => (
<Menu.Item key={item.name}>
{({ active }) => (
<div
className={classNames(
active ? "bg-gray-100" : "",
"block cursor-pointer px-4 py-2 text-sm text-gray-700",
)}
onClick={() => {
navigate(item.href);
}}
>
{item.name}
</div>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</Menu>
<UserMenu
avatarUrl={user.imageUrl}
userName={user.name}
navigation={userNavigation}
/>
</div>
</div>
</div>
Expand Down
71 changes: 11 additions & 60 deletions src/layouts/userLayout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Fragment } from "react";
import { Disclosure, Menu, Transition } from "@headlessui/react";
import { Disclosure } from "@headlessui/react";
import {
Bars3Icon,
BellIcon,
XMarkIcon,
TrophyIcon,
ListBulletIcon,
} from "@heroicons/react/24/outline";
import { ChevronDownIcon } from "@heroicons/react/20/solid";
import UserMenu from "../UserMenu";
import { joinClasses } from "../../utils/common";

const user = {
name: "Tom Cook",
Expand All @@ -29,14 +30,10 @@ const navigation = [
},
];
const userNavigation = [
{ name: "Admin Page", href: "/admin" },
{ name: "Admin page", href: "/admin" },
{ name: "Sign out", href: "#" },
];

function classNames(...classes: any) {
return classes.filter(Boolean).join(" ");
}

export default function Header() {
return (
<Disclosure as="nav" className="h-auto border-b border-gray-200 bg-white">
Expand All @@ -62,7 +59,7 @@ export default function Header() {
<a
key={item.name}
href={item.href}
className={classNames(
className={joinClasses(
item.current
? "border-indigo-500 text-gray-900"
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
Expand All @@ -78,57 +75,11 @@ export default function Header() {
</div>
<div className="hidden sm:ml-6 sm:flex sm:items-center">
{/* Profile dropdown */}
<Menu as="div" className="relative ml-3">
<div>
<Menu.Button className="-m-1.5 flex items-center p-1.5">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full"
src={user.imageUrl}
alt=""
/>
<span className="hidden lg:flex lg:items-center">
<span
className="ml-4 text-sm font-semibold leading-6 text-gray-900"
aria-hidden="true"
>
{user.name}
</span>
<ChevronDownIcon
className="ml-2 h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{userNavigation.map((item) => (
<Menu.Item key={item.name}>
{({ active }) => (
<a
href={item.href}
className={classNames(
active ? "bg-gray-100" : "",
"block px-4 py-2 text-sm text-gray-700",
)}
>
{item.name}
</a>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</Menu>
<UserMenu
userName={user.name}
avatarUrl={user.imageUrl}
navigation={userNavigation}
/>
</div>
<div className="-mr-2 flex items-center sm:hidden">
{/* Mobile menu button */}
Expand All @@ -151,7 +102,7 @@ export default function Header() {
key={item.name}
as="a"
href={item.href}
className={classNames(
className={joinClasses(
item.current
? "border-indigo-500 bg-indigo-50 text-indigo-700"
: "border-transparent text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800",
Expand Down
11 changes: 11 additions & 0 deletions src/utils/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @description
* Join classes together, filtering out any falsey values.
* This function can be especially useful
* when you want to conditionally join classes together.
* @example
* joinClasses("button", disabled && "button--disabled")
*/
export function joinClasses(...classes: any) {
return classes.filter(Boolean).join(" ");
}

0 comments on commit bf677ea

Please sign in to comment.