taskcafe/web/src/shared/components/Card/index.tsx

230 lines
6.5 KiB
TypeScript
Raw Normal View History

import React, { useState, useRef, useEffect } from 'react';
2020-04-10 04:40:22 +02:00
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import TaskAssignee from 'shared/components/TaskAssignee';
2020-04-10 04:40:22 +02:00
import { faPencilAlt, faList } from '@fortawesome/free-solid-svg-icons';
import { faClock, faCheckSquare, faEye } from '@fortawesome/free-regular-svg-icons';
import {
EditorTextarea,
2020-06-19 01:12:15 +02:00
EditorContent,
CompleteIcon,
2020-04-10 04:40:22 +02:00
DescriptionBadge,
DueDateCardBadge,
ListCardBadges,
ListCardBadge,
ListCardBadgeText,
ListCardContainer,
ListCardInnerContainer,
ListCardDetails,
ClockIcon,
ListCardLabels,
ListCardLabel,
ListCardOperation,
CardTitle,
2020-04-21 01:04:27 +02:00
CardMembers,
2020-04-10 04:40:22 +02:00
} from './Styles';
type DueDate = {
isPastDue: boolean;
formattedDate: string;
};
type Checklist = {
complete: number;
total: number;
};
type Props = {
title: string;
taskID: string;
taskGroupID: string;
2020-06-19 01:12:15 +02:00
complete?: boolean;
onContextMenu?: (e: ContextMenuEvent) => void;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
description?: null | string;
2020-04-10 04:40:22 +02:00
dueDate?: DueDate;
checklists?: Checklist;
2020-05-31 06:11:19 +02:00
labels?: Array<ProjectLabel>;
2020-05-31 06:51:22 +02:00
watched?: boolean;
2020-04-10 04:40:22 +02:00
wrapperProps?: any;
2020-04-21 01:04:27 +02:00
members?: Array<TaskUser> | null;
2020-05-27 23:18:50 +02:00
onCardMemberClick?: OnCardMemberClick;
editable?: boolean;
onEditCard?: (taskGroupID: string, taskID: string, cardName: string) => void;
onCardTitleChange?: (name: string) => void;
2020-04-10 04:40:22 +02:00
};
const Card = React.forwardRef(
(
{
wrapperProps,
onContextMenu,
taskID,
taskGroupID,
2020-06-19 01:12:15 +02:00
complete,
2020-04-10 04:40:22 +02:00
onClick,
labels,
title,
dueDate,
description,
checklists,
watched,
2020-04-21 01:04:27 +02:00
members,
2020-05-27 23:18:50 +02:00
onCardMemberClick,
editable,
onEditCard,
onCardTitleChange,
2020-04-10 04:40:22 +02:00
}: Props,
$cardRef: any,
) => {
const [currentCardTitle, setCardTitle] = useState(title);
const $editorRef: any = useRef();
useEffect(() => {
setCardTitle(title);
}, [title]);
useEffect(() => {
if ($editorRef && $editorRef.current) {
$editorRef.current.focus();
$editorRef.current.select();
}
}, []);
const handleKeyDown = (e: any) => {
if (e.key === 'Enter') {
e.preventDefault();
if (onEditCard) {
onEditCard(taskGroupID, taskID, currentCardTitle);
}
}
};
2020-04-10 04:40:22 +02:00
const [isActive, setActive] = useState(false);
const $innerCardRef: any = useRef(null);
const onOpenComposer = () => {
if (typeof $innerCardRef.current !== 'undefined') {
const pos = $innerCardRef.current.getBoundingClientRect();
if (onContextMenu) {
onContextMenu({
2020-06-19 01:12:15 +02:00
width: pos.width,
top: pos.top,
left: pos.left,
taskGroupID,
taskID,
});
}
2020-04-10 04:40:22 +02:00
}
};
const onTaskContext = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onOpenComposer();
};
const onOperationClick = (e: React.MouseEvent<HTMLOrSVGElement>) => {
e.preventDefault();
e.stopPropagation();
onOpenComposer();
};
return (
<ListCardContainer
onMouseEnter={() => setActive(true)}
onMouseLeave={() => setActive(false)}
ref={$cardRef}
onClick={e => {
if (onClick) {
onClick(e);
}
}}
2020-04-10 04:40:22 +02:00
onContextMenu={onTaskContext}
isActive={isActive}
editable={editable}
2020-04-10 04:40:22 +02:00
{...wrapperProps}
>
<ListCardInnerContainer ref={$innerCardRef}>
2020-04-21 01:04:27 +02:00
{isActive && (
<ListCardOperation>
<FontAwesomeIcon onClick={onOperationClick} color="#c2c6dc" size="xs" icon={faPencilAlt} />
</ListCardOperation>
)}
2020-06-19 01:12:15 +02:00
<ListCardDetails complete={complete ?? false}>
2020-04-10 04:40:22 +02:00
<ListCardLabels>
{labels &&
labels.map(label => (
2020-05-31 06:11:19 +02:00
<ListCardLabel color={label.labelColor.colorHex} key={label.id}>
2020-04-10 04:40:22 +02:00
{label.name}
</ListCardLabel>
))}
</ListCardLabels>
{editable ? (
2020-06-19 01:12:15 +02:00
<EditorContent>
{complete && <CompleteIcon width={16} height={16} />}
<EditorTextarea
onChange={e => {
setCardTitle(e.currentTarget.value);
if (onCardTitleChange) {
onCardTitleChange(e.currentTarget.value);
}
}}
onClick={e => {
e.stopPropagation();
}}
onKeyDown={handleKeyDown}
value={currentCardTitle}
ref={$editorRef}
/>
</EditorContent>
) : (
2020-06-19 01:12:15 +02:00
<CardTitle>
{complete && <CompleteIcon width={16} height={16} />}
{title}
</CardTitle>
)}
2020-04-10 04:40:22 +02:00
<ListCardBadges>
{watched && (
<ListCardBadge>
<FontAwesomeIcon color="#6b778c" icon={faEye} size="xs" />
</ListCardBadge>
)}
{dueDate && (
<DueDateCardBadge isPastDue={dueDate.isPastDue}>
<ClockIcon color={dueDate.isPastDue ? '#fff' : '#6b778c'} icon={faClock} size="xs" />
<ListCardBadgeText>{dueDate.formattedDate}</ListCardBadgeText>
</DueDateCardBadge>
)}
{description && (
<DescriptionBadge>
<FontAwesomeIcon color="#6b778c" icon={faList} size="xs" />
</DescriptionBadge>
)}
{checklists && (
<ListCardBadge>
<FontAwesomeIcon color="#6b778c" icon={faCheckSquare} size="xs" />
<ListCardBadgeText>{`${checklists.complete}/${checklists.total}`}</ListCardBadgeText>
</ListCardBadge>
)}
</ListCardBadges>
2020-04-21 01:04:27 +02:00
<CardMembers>
{members &&
members.map(member => (
2020-06-13 00:21:58 +02:00
<TaskAssignee
key={member.id}
size={28}
member={member}
onMemberProfile={$target => {
if (onCardMemberClick) {
onCardMemberClick($target, taskID, member.id);
}
}}
/>
))}
2020-04-21 01:04:27 +02:00
</CardMembers>
2020-04-10 04:40:22 +02:00
</ListCardDetails>
</ListCardInnerContainer>
</ListCardContainer>
);
},
);
Card.displayName = 'Card';
export default Card;