taskcafe/frontend/src/shared/components/TopNavbar/index.tsx

341 lines
9.9 KiB
TypeScript
Raw Normal View History

2020-05-31 06:11:19 +02:00
import React, { useRef, useState, useEffect } from 'react';
import { Home, Star, Bell, AngleDown, BarChart, CheckCircle, ListUnordered } from 'shared/icons';
import styled from 'styled-components';
2020-06-13 00:21:58 +02:00
import ProfileIcon from 'shared/components/ProfileIcon';
import { usePopup } from 'shared/components/PopupMenu';
import { RoleCode } from 'shared/generated/graphql';
import NOOP from 'shared/utils/noop';
import { useHistory } from 'react-router';
2020-04-10 04:40:22 +02:00
import {
2020-08-07 03:50:35 +02:00
TaskcafeLogo,
TaskcafeTitle,
ProjectFinder,
LogoContainer,
NavSeparator,
IconContainerWrapper,
ProjectNameWrapper,
ProjectNameSpan,
2020-05-31 06:11:19 +02:00
ProjectNameTextarea,
2020-05-27 23:18:50 +02:00
InviteButton,
2020-04-10 04:40:22 +02:00
GlobalActions,
ProjectActions,
2020-04-21 01:04:27 +02:00
ProjectMeta,
ProjectName,
ProjectTabs,
ProjectTab,
2020-04-10 04:40:22 +02:00
NavbarWrapper,
NavbarHeader,
2020-05-27 02:53:31 +02:00
ProjectSettingsButton,
2020-04-10 04:40:22 +02:00
ProfileContainer,
ProfileNameWrapper,
ProfileNamePrimary,
ProfileNameSecondary,
ProjectMember,
2020-05-27 23:18:50 +02:00
ProjectMembers,
2020-04-10 04:40:22 +02:00
} from './Styles';
type IconContainerProps = {
disabled?: boolean;
onClick?: ($target: React.RefObject<HTMLElement>) => void;
};
const IconContainer: React.FC<IconContainerProps> = ({ onClick, disabled = false, children }) => {
const $container = useRef<HTMLDivElement>(null);
return (
<IconContainerWrapper
ref={$container}
disabled={disabled}
onClick={() => {
if (onClick) {
onClick($container);
}
}}
>
{children}
</IconContainerWrapper>
);
};
const HomeDashboard = styled(Home)``;
2020-04-10 04:40:22 +02:00
2020-05-31 06:11:19 +02:00
type ProjectHeadingProps = {
onFavorite?: () => void;
name: string;
canEditProjectName: boolean;
2020-05-31 06:11:19 +02:00
onSaveProjectName?: (projectName: string) => void;
onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
2020-05-31 06:11:19 +02:00
};
const ProjectHeading: React.FC<ProjectHeadingProps> = ({
onFavorite,
name: initialProjectName,
onSaveProjectName,
canEditProjectName,
onOpenSettings,
}) => {
2020-05-31 06:11:19 +02:00
const [isEditProjectName, setEditProjectName] = useState(false);
const [projectName, setProjectName] = useState(initialProjectName);
const $projectName = useRef<HTMLInputElement>(null);
2020-05-31 06:11:19 +02:00
useEffect(() => {
if (isEditProjectName && $projectName && $projectName.current) {
$projectName.current.focus();
$projectName.current.select();
}
}, [isEditProjectName]);
useEffect(() => {
setProjectName(initialProjectName);
}, [initialProjectName]);
const onProjectNameChange = (event: React.FormEvent<HTMLInputElement>): void => {
2020-05-31 06:11:19 +02:00
setProjectName(event.currentTarget.value);
};
const onProjectNameBlur = () => {
if (onSaveProjectName) {
onSaveProjectName(projectName);
}
setEditProjectName(false);
};
const onProjectNameKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
if ($projectName && $projectName.current) {
$projectName.current.blur();
}
}
};
const $settings = useRef<HTMLButtonElement>(null);
2020-05-31 06:11:19 +02:00
return (
<>
{isEditProjectName ? (
<ProjectNameWrapper>
<ProjectNameSpan>{projectName}</ProjectNameSpan>
<ProjectNameTextarea
ref={$projectName}
onChange={onProjectNameChange}
onKeyDown={onProjectNameKeyDown}
onBlur={onProjectNameBlur}
spellCheck={false}
value={projectName}
/>
</ProjectNameWrapper>
2020-05-31 06:11:19 +02:00
) : (
<ProjectName
onClick={() => {
if (canEditProjectName) {
setEditProjectName(true);
}
2020-05-31 06:11:19 +02:00
}}
>
{projectName}
</ProjectName>
)}
<ProjectSettingsButton
onClick={() => {
onOpenSettings($settings);
}}
ref={$settings}
>
<AngleDown color="#c2c6dc" />
</ProjectSettingsButton>
{onFavorite && (
<ProjectSettingsButton onClick={() => onFavorite()}>
<Star width={16} height={16} color="#c2c6dc" />
</ProjectSettingsButton>
)}
2020-05-31 06:11:19 +02:00
</>
);
};
export type MenuItem = {
name: string;
link: string;
};
type MenuTypes = {
[key: string]: Array<string>;
};
export const MENU_TYPES: MenuTypes = {
PROJECT_MENU: ['Board', 'Timeline', 'Calender'],
TEAM_MENU: ['Projects', 'Members', 'Settings'],
};
2020-04-10 04:40:22 +02:00
type NavBarProps = {
menuType?: Array<MenuItem> | null;
name: string | null;
currentTab?: number;
onSetTab?: (tab: number) => void;
onOpenProjectFinder: ($target: React.RefObject<HTMLElement>) => void;
onChangeProjectOwner?: (userID: string) => void;
onChangeRole?: (userID: string, roleCode: RoleCode) => void;
onFavorite?: () => void;
2020-06-13 00:21:58 +02:00
onProfileClick: ($target: React.RefObject<HTMLElement>) => void;
onSaveName?: (name: string) => void;
onNotificationClick: ($target: React.RefObject<HTMLElement>) => void;
canEditProjectName?: boolean;
canInviteUser?: boolean;
onInviteUser?: ($target: React.RefObject<HTMLElement>) => void;
onDashboardClick: () => void;
2020-06-13 00:21:58 +02:00
user: TaskUser | null;
onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
2020-05-27 23:18:50 +02:00
projectMembers?: Array<TaskUser> | null;
projectInvitedMembers?: Array<InvitedUser> | null;
onRemoveFromBoard?: (userID: string) => void;
onMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
onInvitedMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, email: string) => void;
2020-12-31 03:56:59 +01:00
onMyTasksClick: () => void;
2020-04-10 04:40:22 +02:00
};
2020-05-31 06:11:19 +02:00
2020-04-21 01:04:27 +02:00
const NavBar: React.FC<NavBarProps> = ({
menuType,
canInviteUser = false,
onInviteUser,
onChangeProjectOwner,
currentTab,
onMemberProfile,
onInvitedMemberProfile,
canEditProjectName = false,
onOpenProjectFinder,
onFavorite,
onSetTab,
projectInvitedMembers,
onChangeRole,
name,
onRemoveFromBoard,
onSaveName,
2020-04-21 01:04:27 +02:00
onProfileClick,
onNotificationClick,
onDashboardClick,
2020-12-31 03:56:59 +01:00
onMyTasksClick,
2020-06-13 00:21:58 +02:00
user,
2020-05-27 23:18:50 +02:00
projectMembers,
onOpenSettings,
2020-04-21 01:04:27 +02:00
}) => {
2020-06-13 00:21:58 +02:00
const handleProfileClick = ($target: React.RefObject<HTMLElement>) => {
if ($target && $target.current) {
onProfileClick($target);
}
2020-04-10 04:40:22 +02:00
};
const history = useHistory();
2020-05-27 23:18:50 +02:00
const { showPopup } = usePopup();
2020-04-10 04:40:22 +02:00
return (
<NavbarWrapper>
<NavbarHeader>
<ProjectActions>
2020-04-21 01:04:27 +02:00
<ProjectMeta>
{name && (
<ProjectHeading
onFavorite={onFavorite}
onOpenSettings={onOpenSettings}
name={name}
canEditProjectName={canEditProjectName}
onSaveProjectName={onSaveName}
/>
)}
2020-04-21 01:04:27 +02:00
</ProjectMeta>
{name && (
<ProjectTabs>
{menuType &&
menuType.map((menu, idx) => {
return (
<ProjectTab
key={menu.name}
to={menu.link}
exact
onClick={() => {
// TODO
}}
>
{menu.name}
</ProjectTab>
);
})}
</ProjectTabs>
)}
2020-04-10 04:40:22 +02:00
</ProjectActions>
<LogoContainer to="/">
2020-08-07 03:50:35 +02:00
<TaskcafeLogo width={32} height={32} />
<TaskcafeTitle>Taskcafé</TaskcafeTitle>
</LogoContainer>
2020-04-10 04:40:22 +02:00
<GlobalActions>
{projectMembers && projectInvitedMembers && onMemberProfile && onInvitedMemberProfile && (
<>
<ProjectMembers>
{projectMembers.map((member, idx) => (
<ProjectMember
showRoleIcons
zIndex={projectMembers.length - idx + projectInvitedMembers.length}
key={member.id}
size={28}
member={member}
onMemberProfile={onMemberProfile}
/>
))}
{projectInvitedMembers.map((member, idx) => (
<ProjectMember
showRoleIcons
zIndex={projectInvitedMembers.length - idx}
key={member.email}
size={28}
invited
member={{
id: member.email,
fullName: member.email,
profileIcon: {
url: null,
initials: member.email.charAt(0),
bgColor: '#fff',
},
}}
onMemberProfile={onInvitedMemberProfile}
/>
))}
{canInviteUser && (
<InviteButton
onClick={$target => {
if (onInviteUser) {
onInviteUser($target);
}
}}
variant="outline"
>
Invite
</InviteButton>
)}
</ProjectMembers>
<NavSeparator />
</>
2020-05-27 23:18:50 +02:00
)}
<ProjectFinder onClick={onOpenProjectFinder} variant="gradient">
Projects
</ProjectFinder>
<IconContainer onClick={() => onDashboardClick()}>
<HomeDashboard width={20} height={20} />
</IconContainer>
2020-12-31 03:56:59 +01:00
<IconContainer onClick={() => onMyTasksClick()}>
<CheckCircle width={20} height={20} />
</IconContainer>
<IconContainer disabled onClick={NOOP}>
<ListUnordered width={20} height={20} />
</IconContainer>
<IconContainer disabled onClick={onNotificationClick}>
2020-04-10 04:40:22 +02:00
<Bell color="#c2c6dc" size={20} />
</IconContainer>
<IconContainer disabled onClick={NOOP}>
<BarChart width={20} height={20} />
</IconContainer>
2020-06-13 00:21:58 +02:00
{user && (
<IconContainer>
<ProfileIcon user={user} size={30} onProfileClick={handleProfileClick} />
</IconContainer>
2020-06-13 00:21:58 +02:00
)}
2020-04-10 04:40:22 +02:00
</GlobalActions>
</NavbarHeader>
</NavbarWrapper>
);
};
export default NavBar;