feat: add comments badge to task card

This commit is contained in:
Jordan Knott
2021-10-25 15:14:24 -05:00
parent 3992e4c2de
commit 119a4b2868
18 changed files with 385 additions and 100 deletions

View File

@@ -1,18 +1,27 @@
import styled, { css, keyframes } from 'styled-components';
import { mixin } from 'shared/utils/styles';
import TextareaAutosize from 'react-autosize-textarea';
import { CheckCircle, CheckSquareOutline, Clock } from 'shared/icons';
import { CheckCircle, CheckSquareOutline, Clock, Bubble } from 'shared/icons';
import TaskAssignee from 'shared/components/TaskAssignee';
export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
box-shadow: 0 0 0 2px ${props => props.theme.colors.bg.secondary},
inset 0 0 0 1px ${props => mixin.rgba(props.theme.colors.bg.secondary, 0.07)};
z-index: ${props => props.zIndex};
box-shadow: 0 0 0 2px ${(props) => props.theme.colors.bg.secondary},
inset 0 0 0 1px ${(props) => mixin.rgba(props.theme.colors.bg.secondary, 0.07)};
z-index: ${(props) => props.zIndex};
position: relative;
`;
export const CommentsIcon = styled(Bubble)<{ color: 'success' | 'normal' }>`
${(props) =>
props.color === 'success' &&
css`
fill: ${props.theme.colors.success};
stroke: ${props.theme.colors.success};
`}
`;
export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'normal' }>`
${props =>
${(props) =>
props.color === 'success' &&
css`
fill: ${props.theme.colors.success};
@@ -21,7 +30,7 @@ export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'no
`;
export const ClockIcon = styled(Clock)<{ color: string }>`
fill: ${props => props.color};
fill: ${(props) => props.color};
`;
export const EditorTextarea = styled(TextareaAutosize)`
@@ -40,7 +49,7 @@ export const EditorTextarea = styled(TextareaAutosize)`
padding: 0;
font-size: 14px;
line-height: 18px;
color: ${props => props.theme.colors.text.primary};
color: ${(props) => props.theme.colors.text.primary};
&:focus {
border: none;
outline: none;
@@ -54,6 +63,22 @@ export const ListCardBadges = styled.div`
margin-left: -2px;
`;
export const CommentsBadge = styled.div`
color: #5e6c84;
display: flex;
align-items: center;
margin: 0 6px 4px 0;
font-size: 12px;
max-width: 100%;
min-height: 20px;
overflow: hidden;
position: relative;
padding: 2px;
text-decoration: none;
text-overflow: ellipsis;
vertical-align: top;
`;
export const ListCardBadge = styled.div`
color: #5e6c84;
display: flex;
@@ -76,7 +101,7 @@ export const DescriptionBadge = styled(ListCardBadge)`
export const DueDateCardBadge = styled(ListCardBadge)<{ isPastDue: boolean }>`
font-size: 12px;
${props =>
${(props) =>
props.isPastDue &&
css`
padding-left: 4px;
@@ -91,7 +116,7 @@ export const ListCardBadgeText = styled.span<{ color?: 'success' | 'normal' }>`
padding: 0 4px 0 6px;
vertical-align: top;
white-space: nowrap;
${props => props.color === 'success' && `color: ${props.theme.colors.success};`}
${(props) => props.color === 'success' && `color: ${props.theme.colors.success};`}
`;
export const ListCardContainer = styled.div<{ isActive: boolean; editable: boolean }>`
@@ -102,7 +127,7 @@ export const ListCardContainer = styled.div<{ isActive: boolean; editable: boole
cursor: pointer !important;
position: relative;
background-color: ${props =>
background-color: ${(props) =>
props.isActive && !props.editable
? mixin.darken(props.theme.colors.bg.secondary, 0.1)
: `${props.theme.colors.bg.secondary}`};
@@ -119,7 +144,7 @@ export const ListCardDetails = styled.div<{ complete: boolean }>`
position: relative;
z-index: 10;
${props => props.complete && 'opacity: 0.6;'}
${(props) => props.complete && 'opacity: 0.6;'}
`;
const labelVariantExpandAnimation = keyframes`
@@ -157,7 +182,7 @@ export const ListCardLabelsWrapper = styled.div`
`;
export const ListCardLabel = styled.span<{ variant: 'small' | 'large' }>`
${props =>
${(props) =>
props.variant === 'small'
? css`
height: 8px;
@@ -183,14 +208,14 @@ export const ListCardLabel = styled.span<{ variant: 'small' | 'large' }>`
color: #fff;
display: flex;
position: relative;
background-color: ${props => props.color};
background-color: ${(props) => props.color};
`;
export const ListCardLabels = styled.div<{ toggleLabels: boolean; toggleDirection: 'expand' | 'shrink' }>`
&:hover {
opacity: 0.8;
}
${props =>
${(props) =>
props.toggleLabels &&
props.toggleDirection === 'expand' &&
css`
@@ -201,7 +226,7 @@ export const ListCardLabels = styled.div<{ toggleLabels: boolean; toggleDirectio
animation: ${labelTextVariantExpandAnimation} 0.45s ease-out;
}
`}
${props =>
${(props) =>
props.toggleLabels &&
props.toggleDirection === 'shrink' &&
css`
@@ -225,7 +250,7 @@ export const ListCardOperation = styled.span`
top: 2px;
z-index: 100;
&:hover {
background-color: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.25)};
background-color: ${(props) => mixin.darken(props.theme.colors.bg.secondary, 0.25)};
}
`;
@@ -234,7 +259,7 @@ export const CardTitle = styled.div`
margin: 0 0 4px;
overflow: hidden;
text-decoration: none;
color: ${props => props.theme.colors.text.primary};
color: ${(props) => props.theme.colors.text.primary};
display: block;
align-items: center;
`;
@@ -251,7 +276,7 @@ export const CardMembers = styled.div`
`;
export const CompleteIcon = styled(CheckCircle)`
fill: ${props => props.theme.colors.success};
fill: ${(props) => props.theme.colors.success};
margin-right: 4px;
flex-shrink: 0;
margin-bottom: -2px;

View File

@@ -23,6 +23,8 @@ import {
CardTitle,
CardMembers,
CardTitleText,
CommentsIcon,
CommentsBadge,
} from './Styles';
type DueDate = {
@@ -47,6 +49,7 @@ type Props = {
dueDate?: DueDate;
checklists?: Checklist | null;
labels?: Array<ProjectLabel>;
comments?: { unread: boolean; total: number } | null;
watched?: boolean;
wrapperProps?: any;
members?: Array<TaskUser> | null;
@@ -72,6 +75,7 @@ const Card = React.forwardRef(
taskGroupID,
complete,
toggleLabels = false,
comments,
toggleDirection = 'shrink',
setToggleLabels,
onClick,
@@ -138,7 +142,7 @@ const Card = React.forwardRef(
onMouseEnter={() => setActive(true)}
onMouseLeave={() => setActive(false)}
ref={$cardRef}
onClick={e => {
onClick={(e) => {
if (onClick) {
onClick(e);
}
@@ -151,7 +155,7 @@ const Card = React.forwardRef(
<ListCardInnerContainer ref={$innerCardRef}>
{!isPublic && isActive && !editable && (
<ListCardOperation
onClick={e => {
onClick={(e) => {
e.stopPropagation();
if (onContextMenu) {
onContextMenu($innerCardRef, taskID, taskGroupID);
@@ -167,7 +171,7 @@ const Card = React.forwardRef(
<ListCardLabels
toggleLabels={toggleLabels}
toggleDirection={toggleDirection}
onClick={e => {
onClick={(e) => {
e.stopPropagation();
if (onCardLabelClick) {
onCardLabelClick();
@@ -177,7 +181,7 @@ const Card = React.forwardRef(
{labels
.slice()
.sort((a, b) => a.labelColor.position - b.labelColor.position)
.map(label => (
.map((label) => (
<ListCardLabel
onAnimationEnd={() => {
if (setToggleLabels) {
@@ -198,13 +202,13 @@ const Card = React.forwardRef(
<EditorContent>
{complete && <CompleteIcon width={16} height={16} />}
<EditorTextarea
onChange={e => {
onChange={(e) => {
setCardTitle(e.currentTarget.value);
if (onCardTitleChange) {
onCardTitleChange(e.currentTarget.value);
}
}}
onClick={e => {
onClick={(e) => {
e.stopPropagation();
}}
onKeyDown={handleKeyDown}
@@ -235,6 +239,12 @@ const Card = React.forwardRef(
<List width={8} height={8} />
</DescriptionBadge>
)}
{comments && (
<CommentsBadge>
<CommentsIcon color={comments.unread ? 'success' : 'normal'} width={8} height={8} />
<ListCardBadgeText color={comments.unread ? 'success' : 'normal'}>{comments.total}</ListCardBadgeText>
</CommentsBadge>
)}
{checklists && (
<ListCardBadge>
<ChecklistIcon
@@ -256,7 +266,7 @@ const Card = React.forwardRef(
size={28}
zIndex={members.length - idx}
member={member}
onMemberProfile={$target => {
onMemberProfile={($target) => {
if (onCardMemberClick) {
onCardMemberClick($target, taskID, member.id);
}