feat: add task details

This commit is contained in:
Jordan Knott 2020-09-02 20:25:28 -05:00
parent a9a1576f46
commit 771d598c04
28 changed files with 1470 additions and 683 deletions

View File

@ -59,6 +59,7 @@
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-scripts": "3.4.0", "react-scripts": "3.4.0",
"react-select": "^3.1.0", "react-select": "^3.1.0",
"rich-markdown-editor": "^10.6.5",
"styled-components": "^5.0.1", "styled-components": "^5.0.1",
"typescript": "~3.7.2" "typescript": "~3.7.2"
}, },
@ -95,10 +96,10 @@
"@graphql-codegen/typescript-operations": "^1.13.2", "@graphql-codegen/typescript-operations": "^1.13.2",
"@graphql-codegen/typescript-react-apollo": "^1.13.2", "@graphql-codegen/typescript-react-apollo": "^1.13.2",
"@storybook/addon-actions": "^5.3.13", "@storybook/addon-actions": "^5.3.13",
"@storybook/addon-links": "^5.3.13",
"@storybook/addon-backgrounds": "^5.3.17", "@storybook/addon-backgrounds": "^5.3.17",
"@storybook/addon-docs": "^5.3.17", "@storybook/addon-docs": "^5.3.17",
"@storybook/addon-knobs": "^5.3.17", "@storybook/addon-knobs": "^5.3.17",
"@storybook/addon-links": "^5.3.13",
"@storybook/addon-storysource": "^5.3.17", "@storybook/addon-storysource": "^5.3.17",
"@storybook/addon-viewport": "^5.3.17", "@storybook/addon-viewport": "^5.3.17",
"@storybook/addons": "^5.3.13", "@storybook/addons": "^5.3.13",

View File

@ -298,13 +298,14 @@ const Details: React.FC<DetailsProps> = ({
return ( return (
<> <>
<Modal <Modal
width={768} width={1070}
onClose={() => { onClose={() => {
history.push(projectURL); history.push(projectURL);
}} }}
renderContent={() => { renderContent={() => {
return ( return (
<TaskDetails <TaskDetails
me={data.me.user}
task={data.findTask} task={data.findTask}
onChecklistDrop={checklist => { onChecklistDrop={checklist => {
updateTaskChecklistLocation({ updateTaskChecklistLocation({

View File

@ -270,7 +270,17 @@ const Project = () => {
updateTaskName({ variables: { taskID: updatedTask.id, name: newName } }); updateTaskName({ variables: { taskID: updatedTask.id, name: newName } });
}} }}
onTaskDescriptionChange={(updatedTask, newDescription) => { onTaskDescriptionChange={(updatedTask, newDescription) => {
updateTaskDescription({ variables: { taskID: updatedTask.id, description: newDescription } }); updateTaskDescription({
variables: { taskID: updatedTask.id, description: newDescription },
optimisticResponse: {
__typename: 'Mutation',
updateTaskDescription: {
__typename: 'Task',
id: updatedTask.id,
description: newDescription,
},
},
});
}} }}
onDeleteTask={deletedTask => { onDeleteTask={deletedTask => {
deleteTask({ variables: { taskID: deletedTask.id } }); deleteTask({ variables: { taskID: deletedTask.id } });

View File

@ -98,7 +98,7 @@ const AddList: React.FC<AddListProps> = ({ onSave }) => {
) : ( ) : (
<Placeholder> <Placeholder>
<AddIconWrapper> <AddIconWrapper>
<Plus size={12} color="#c2c6dc" /> <Plus width={12} height={12} />
</AddIconWrapper> </AddIconWrapper>
Add another list Add another list
</Placeholder> </Placeholder>

View File

@ -1,7 +1,7 @@
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import styled, { css } from 'styled-components/macro'; import styled, { css } from 'styled-components/macro';
const Text = styled.span<{ fontSize: string; justifyTextContent: string }>` const Text = styled.span<{ fontSize: string; justifyTextContent: string; hasIcon?: boolean }>`
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
@ -9,6 +9,11 @@ const Text = styled.span<{ fontSize: string; justifyTextContent: string }>`
transition: all 0.2s ease; transition: all 0.2s ease;
font-size: ${props => props.fontSize}; font-size: ${props => props.fontSize};
color: rgba(${props => props.theme.colors.text.secondary}); color: rgba(${props => props.theme.colors.text.secondary});
${props =>
props.hasIcon &&
css`
padding-left: 4px;
`}
`; `;
const Base = styled.button<{ color: string; disabled: boolean }>` const Base = styled.button<{ color: string; disabled: boolean }>`
@ -18,6 +23,8 @@ const Base = styled.button<{ color: string; disabled: boolean }>`
cursor: pointer; cursor: pointer;
padding: 0.75rem 2rem; padding: 0.75rem 2rem;
border-radius: ${props => props.theme.borderRadius.alternate}; border-radius: ${props => props.theme.borderRadius.alternate};
display: flex;
align-items: center;
${props => ${props =>
props.disabled && props.disabled &&
@ -34,16 +41,28 @@ const Filled = styled(Base)`
box-shadow: 0 8px 25px -8px rgba(${props => props.theme.colors[props.color]}); box-shadow: 0 8px 25px -8px rgba(${props => props.theme.colors[props.color]});
} }
`; `;
const Outline = styled(Base)` const Outline = styled(Base)<{ invert: boolean }>`
border: 1px solid rgba(${props => props.theme.colors[props.color]}); border: 1px solid rgba(${props => props.theme.colors[props.color]});
background: transparent; background: transparent;
${props =>
props.invert
? css`
background: rgba(${props.theme.colors[props.color]});
& ${Text} { & ${Text} {
color: rgba(${props => props.theme.colors[props.color]}); color: rgba(${props.theme.colors.text.secondary});
} }
&:hover { &:hover {
background: rgba(${props => props.theme.colors[props.color]}, 0.08); background: rgba(${props.theme.colors[props.color]}, 0.8);
} }
`
: css`
& ${Text} {
color: rgba(${props.theme.colors[props.color]});
}
&:hover {
background: rgba(${props.theme.colors[props.color]}, 0.08);
}
`}
`; `;
const Flat = styled(Base)` const Flat = styled(Base)`
@ -110,6 +129,8 @@ type ButtonProps = {
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark'; color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
disabled?: boolean; disabled?: boolean;
type?: 'button' | 'submit'; type?: 'button' | 'submit';
icon?: JSX.Element;
invert?: boolean;
className?: string; className?: string;
onClick?: ($target: React.RefObject<HTMLButtonElement>) => void; onClick?: ($target: React.RefObject<HTMLButtonElement>) => void;
justifyTextContent?: string; justifyTextContent?: string;
@ -118,10 +139,12 @@ type ButtonProps = {
const Button: React.FC<ButtonProps> = ({ const Button: React.FC<ButtonProps> = ({
disabled = false, disabled = false,
fontSize = '14px', fontSize = '14px',
invert = false,
color = 'primary', color = 'primary',
variant = 'filled', variant = 'filled',
type = 'button', type = 'button',
justifyTextContent = 'center', justifyTextContent = 'center',
icon,
onClick, onClick,
className, className,
children, children,
@ -136,7 +159,8 @@ const Button: React.FC<ButtonProps> = ({
case 'filled': case 'filled':
return ( return (
<Filled ref={$button} type={type} onClick={handleClick} className={className} disabled={disabled} color={color}> <Filled ref={$button} type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
<Text justifyTextContent={justifyTextContent} fontSize={fontSize}> {icon && icon}
<Text hasIcon={typeof icon !== 'undefined'} justifyTextContent={justifyTextContent} fontSize={fontSize}>
{children} {children}
</Text> </Text>
</Filled> </Filled>
@ -145,6 +169,7 @@ const Button: React.FC<ButtonProps> = ({
return ( return (
<Outline <Outline
ref={$button} ref={$button}
invert={invert}
type={type} type={type}
onClick={handleClick} onClick={handleClick}
className={className} className={className}

View File

@ -25,7 +25,7 @@ const WindowTitle = styled.div`
const WindowTitleIcon = styled(CheckSquareOutline)` const WindowTitleIcon = styled(CheckSquareOutline)`
top: 10px; top: 10px;
left: -40px; left: -32px;
position: absolute; position: absolute;
`; `;

View File

@ -102,7 +102,7 @@ const List = React.forwardRef(
{children && children} {children && children}
<AddCardContainer hidden={isComposerOpen}> <AddCardContainer hidden={isComposerOpen}>
<AddCardButton onClick={() => onOpenComposer(id)}> <AddCardButton onClick={() => onOpenComposer(id)}>
<Plus size={12} color="#c2c6dc" /> <Plus width={12} height={12} />
<AddCardButtonText>Add another card</AddCardButtonText> <AddCardButtonText>Add another card</AddCardButtonText>
</AddCardButton> </AddCardButton>
</AddCardContainer> </AddCardContainer>

View File

@ -4,10 +4,8 @@ export const Container = styled.div`
flex: 1; flex: 1;
user-select: none; user-select: none;
white-space: nowrap; white-space: nowrap;
margin-bottom: 8px;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
padding-bottom: 8px;
::-webkit-scrollbar { ::-webkit-scrollbar {
height: 10px; height: 10px;
@ -35,10 +33,9 @@ export const BoardWrapper = styled.div`
user-select: none; user-select: none;
white-space: nowrap; white-space: nowrap;
margin-bottom: 8px;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
padding-bottom: 8px; padding-bottom: 4px;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;

View File

@ -44,6 +44,7 @@ type MemberProps = {
showName?: boolean; showName?: boolean;
className?: string; className?: string;
showCheckmark?: boolean; showCheckmark?: boolean;
size?: number;
}; };
const CardMemberWrapper = styled.div<{ ref: any }>` const CardMemberWrapper = styled.div<{ ref: any }>`
@ -63,6 +64,7 @@ const Member: React.FC<MemberProps> = ({
showName, showName,
showCheckmark = false, showCheckmark = false,
className, className,
size = 28,
}) => { }) => {
const $targetRef = useRef<HTMLDivElement>(); const $targetRef = useRef<HTMLDivElement>();
return ( return (
@ -77,7 +79,7 @@ const Member: React.FC<MemberProps> = ({
} }
}} }}
> >
<TaskAssignee onMemberProfile={NOOP} size={28} member={member} /> <TaskAssignee onMemberProfile={NOOP} size={32} member={member} />
{showName && <CardMemberName>{member.fullName}</CardMemberName>} {showName && <CardMemberName>{member.fullName}</CardMemberName>}
{showCheckmark && <CardCheckmark width={12} height={12} />} {showCheckmark && <CardCheckmark width={12} height={12} />}
</CardMemberWrapper> </CardMemberWrapper>

View File

@ -15,18 +15,20 @@ export const ScrollOverlay = styled.div`
export const ClickableOverlay = styled.div` export const ClickableOverlay = styled.div`
min-height: 100%; min-height: 100%;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
`; `;
export const StyledModal = styled.div<{ width: number }>` export const StyledModal = styled.div<{ width: number; height: number }>`
display: inline-block;
position: relative; position: relative;
margin: 48px 0 80px; width: ${props => props.width}px;
width: 100%; height: ${props => props.height}px;
left: 0;
right: 0;
top: 48px;
bottom: 16px;
margin: auto;
background: #262c49; background: #262c49;
max-width: ${props => props.width}px;
vertical-align: middle; vertical-align: middle;
border-radius: 3px; border-radius: 6px;
${mixin.boxShadowMedium} ${mixin.boxShadowMedium}
`; `;

View File

@ -1,9 +1,10 @@
import React, { useRef } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import useOnOutsideClick from 'shared/hooks/onOutsideClick'; import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown'; import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
import useWindowSize from 'shared/hooks/useWindowSize';
import styled from 'styled-components';
import { Cross } from 'shared/icons';
import { ScrollOverlay, ClickableOverlay, StyledModal } from './Styles'; import { ScrollOverlay, ClickableOverlay, StyledModal } from './Styles';
const $root: HTMLElement = document.getElementById('root')!; // eslint-disable-line @typescript-eslint/no-non-null-assertion const $root: HTMLElement = document.getElementById('root')!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
@ -14,21 +15,50 @@ type ModalProps = {
renderContent: () => JSX.Element; renderContent: () => JSX.Element;
}; };
const Modal: React.FC<ModalProps> = ({ width, onClose: tellParentToClose, renderContent }) => { function getAdjustedHeight(height: number) {
if (height >= 900) {
return height - 150;
}
if (height >= 800) {
return height - 125;
}
return height - 70;
}
const CloseIcon = styled(Cross)`
position: absolute;
top: 16px;
right: -32px;
cursor: pointer;
fill: rgba(${props => props.theme.colors.text.primary});
&:hover {
fill: rgba(${props => props.theme.colors.text.secondary});
}
`;
const InnerModal: React.FC<ModalProps> = ({ width, onClose: tellParentToClose, renderContent }) => {
const $modalRef = useRef<HTMLDivElement>(null); const $modalRef = useRef<HTMLDivElement>(null);
const $clickableOverlayRef = useRef<HTMLDivElement>(null); const $clickableOverlayRef = useRef<HTMLDivElement>(null);
const [_width, height] = useWindowSize();
useOnOutsideClick($modalRef, true, tellParentToClose, $clickableOverlayRef); useOnOutsideClick($modalRef, true, tellParentToClose, $clickableOverlayRef);
useOnEscapeKeyDown(true, tellParentToClose); useOnEscapeKeyDown(true, tellParentToClose);
return ReactDOM.createPortal( return (
<ScrollOverlay> <ScrollOverlay>
<ClickableOverlay ref={$clickableOverlayRef}> <ClickableOverlay ref={$clickableOverlayRef}>
<StyledModal width={width} ref={$modalRef}> <StyledModal width={width} height={getAdjustedHeight(height)} ref={$modalRef}>
{renderContent()} {renderContent()}
<CloseIcon onClick={() => tellParentToClose()} width={20} height={20} />
</StyledModal> </StyledModal>
</ClickableOverlay> </ClickableOverlay>
</ScrollOverlay>, </ScrollOverlay>
);
};
const Modal: React.FC<ModalProps> = ({ width, onClose: tellParentToClose, renderContent }) => {
return ReactDOM.createPortal(
<InnerModal width={width} onClose={tellParentToClose} renderContent={renderContent} />,
$root, $root,
); );
}; };

View File

@ -13,7 +13,7 @@ export const AddProjectItem: React.FC<AddProjectItemProps> = ({ onAddProject })
onAddProject(); onAddProject();
}} }}
> >
<Plus size={20} color="#c2c6dc" /> <Plus width={12} height={12} />
<AddProjectLabel>New Project</AddProjectLabel> <AddProjectLabel>New Project</AddProjectLabel>
</AddProjectWrapper> </AddProjectWrapper>
); );

View File

@ -1,288 +1,365 @@
import styled from 'styled-components'; import styled, { css } from 'styled-components';
import TextareaAutosize from 'react-autosize-textarea/lib'; import TextareaAutosize from 'react-autosize-textarea';
import { mixin } from 'shared/utils/styles'; import { mixin } from 'shared/utils/styles';
import Button from 'shared/components/Button'; import Button from 'shared/components/Button';
import TaskAssignee from 'shared/components/TaskAssignee'; import TaskAssignee from 'shared/components/TaskAssignee';
import { User, Trash, Paperclip } from 'shared/icons';
import Member from 'shared/components/Member';
export const TaskHeader = styled.div` export const Container = styled.div`
padding: 21px 30px 0px; display: flex;
margin-right: 70px; height: 100%;
`;
export const LeftSidebar = styled.div`
display: flex;
flex-direction: column;
width: 300px;
background: #222740;
`;
export const MarkCompleteButton = styled.button<{ invert: boolean }>`
padding: 4px 8px;
position: relative;
border: none;
cursor: pointer;
border-radius: ${props => props.theme.borderRadius.alternate};
display: flex;
align-items: center;
background: transparent;
& span {
margin-left: 4px;
}
${props =>
props.invert
? css`
background: rgba(${props.theme.colors.success});
& svg {
fill: rgba(${props.theme.colors.text.secondary});
}
& span {
color: rgba(${props.theme.colors.text.secondary});
}
&:hover {
background: rgba(${props.theme.colors.success}, 0.8);
}
`
: css`
background: none;
border: 1px solid rgba(${props.theme.colors.text.secondary});
& svg {
fill: rgba(${props.theme.colors.text.secondary});
}
& span {
color: rgba(${props.theme.colors.text.secondary});
}
&:hover {
background: rgba(${props.theme.colors.success}, 0.08);
border: 1px solid rgba(${props.theme.colors.success});
}
&:hover svg {
fill: rgba(${props.theme.colors.success});
}
&:hover span {
color: rgba(${props.theme.colors.success});
}
`}
`;
export const LeftSidebarContent = styled.div`
padding-top: 32px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
`; `;
export const TaskMeta = styled.div` export const LeftSidebarSection = styled.div`
position: relative; display: flex;
flex-direction: column;
padding-left: 32px;
padding-right: 32px;
padding-bottom: 16px;
border-bottom: 1px solid #414561;
`;
export const SidebarTitle = styled.div`
font-size: 12px;
min-height: 24px;
margin-left: 8px;
color: rgba(${props => props.theme.colors.text.primary}, 0.75);
padding-top: 4px;
letter-spacing: 0.5px;
text-transform: uppercase;
`;
export const SidebarButton = styled.div`
font-size: 14px;
color: rgba(${props => props.theme.colors.text.primary});
min-height: 32px;
width: 100%;
padding: 9px 8px 7px 8px;
border-color: transparent;
border-radius: 6px;
border-width: 1px;
border-style: solid;
display: inline-block;
outline: 0;
cursor: pointer; cursor: pointer;
font-size: 14px; &:hover {
border-color: #414561;
}
`;
export const SidebarButtonText = styled.span`
min-height: 16px;
flex: 1 1 auto;
display: flex; display: flex;
align-items: center; justify-content: flex-start;
border-radius: 4px;
`; `;
export const TaskGroupLabel = styled.span` export const ContentContainer = styled.div`
color: #c2c6dc;
font-size: 14px;
`;
export const TaskGroupLabelName = styled.span`
color: #c2c6dc;
text-decoration: underline;
font-size: 14px;
`;
export const TaskActions = styled.div`
position: absolute;
top: 0;
right: 0;
padding: 21px 18px 0px;
display: flex; display: flex;
align-items: center; flex-direction: column;
`;
export const TaskAction = styled.button`
display: inline-flex;
align-items: center;
justify-content: center;
height: 32px;
cursor: pointer;
padding: 0px 9px;
`;
export const TaskDetailsWrapper = styled.div`
display: flex;
padding: 0px 16px 60px;
`;
export const TaskDetailsContent = styled.div`
flex: 1; flex: 1;
padding-right: 8px; padding-top: 32px;
overflow: auto;
`; `;
export const TaskDetailsSidebar = styled.div` export const HeaderContainer = styled.div`
width: 168px; flex: 0 0 auto;
padding-left: 8px; padding: 0px 32px 0px 24px;
`;
export const HeaderInnerContainer = styled.div`
display: flex;
justify-content: space-between;
margin: 0 0 0 0;
padding: 0 0 0 4px;
`;
export const HeaderLeft = styled.div`
display: flex;
justify-content: flex-start;
`; `;
export const TaskDetailsTitleWrapper = styled.div` export const TaskDetailsTitleWrapper = styled.div`
width: 100%; width: 100%;
margin: 0 0 0 -8px; margin: 8px 0 4px 0;
display: inline-block; display: inline-block;
`; `;
export const TaskDetailsTitle = styled(TextareaAutosize)` export const TaskDetailsTitle = styled(TextareaAutosize)`
line-height: 1.28; padding: 9px 8px 7px 8px;
resize: none; border-color: transparent;
box-shadow: transparent 0px 0px 0px 1px; border-radius: 6px;
font-size: 24px;
font-weight: 700;
padding: 4px;
background: #262c49;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
border-color: transparent;
border-image: initial;
transition: background 0.1s ease 0s;
overflow-y: hidden;
width: 100%; width: 100%;
color: #c2c6dc; color: #c2c6dc;
&:focus { display: inline-block;
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px; outline: 0;
background: ${mixin.darken('#262c49', 0.15)};
}
`;
export const TaskDetailsLabel = styled.div`
padding: 24px 0px 12px;
font-size: 15px;
font-weight: 600;
color: #c2c6dc;
`;
export const TaskDetailsAddDetailsButton = styled.div`
background: ${mixin.darken('#262c49', 0.15)};
box-shadow: none;
border: none;
border-radius: 3px;
display: block;
min-height: 56px;
padding: 8px 12px;
text-decoration: none;
font-size: 14px;
cursor: pointer;
color: #c2c6dc;
&:hover {
background: ${mixin.darken('#262c49', 0.25)};
box-shadow: none;
border: none;
}
`;
export const TaskDetailsEditorWrapper = styled.div`
display: block;
padding-bottom: 9px;
z-index: 50;
width: 100%;
`;
export const TaskDetailsEditor = styled(TextareaAutosize)`
width: 100%;
min-height: 108px;
color: #c2c6dc;
background: #262c49;
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
border-radius: 3px;
line-height: 20px;
padding: 8px 12px;
outline: none;
border: none;
&:focus {
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
background: ${mixin.darken('#262c49', 0.05)};
}
`;
export const TaskDetailsMarkdown = styled.div`
width: 100%;
cursor: pointer;
color: #c2c6dc;
h1 {
font-size: 24px; font-size: 24px;
font-weight: 600;
line-height: 28px;
margin: 0 0 12px;
}
h2 {
font-weight: 600;
font-size: 20px;
line-height: 24px;
margin: 16px 0 8px;
}
p {
margin: 0 0 8px;
}
strong {
font-weight: 700; font-weight: 700;
background: none;
&:hover {
border-color: #414561;
} }
&:focus {
border-color: rgba(${props => props.theme.colors.primary});
}
`;
export const DueDateTitle = styled.div`
font-size: 12px;
min-height: 24px;
margin-left: 8px;
color: rgba(${props => props.theme.colors.text.primary}, 0.75);
padding-top: 8px;
letter-spacing: 0.5px;
text-transform: uppercase;
`;
export const AssignedUsersSection = styled.div`
padding-left: 32px;
padding-right: 32px;
padding-top: 24px;
padding-bottom: 24px;
border-bottom: 1px solid #414561;
display: flex;
flex-direction: column;
`;
export const AssignUserIcon = styled.div`
cursor: pointer;
height: 32px;
width: 32px;
position: relative;
border-radius: 50%;
border: 1px dashed #414561;
margin-right: 8px;
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
&:hover {
border: 1px solid rgba(${props => props.theme.colors.text.secondary}, 0.75);
}
&:hover svg {
fill: rgba(${props => props.theme.colors.text.secondary}, 0.75);
}
`;
export const AssignUsersButton = styled.div`
cursor: pointer;
display: inline-flex;
flex: 1 1 auto;
height: 40px;
padding: 4px 16px 4px 8px;
margin-left: -1px;
border-radius: 6px;
align-items: center;
border: 1px solid transparent;
&:hover {
border: 1px solid ${mixin.darken('#414561', 0.15)};
}
&:hover ${AssignUserIcon} {
border: 1px solid #414561;
}
`;
export const AssignUserLabel = styled.span`
flex: 1 1 auto;
line-height: 15px;
color: rgba(${props => props.theme.colors.text.primary}, 0.75);
`;
export const ExtraActionsSection = styled.div`
padding-left: 32px;
padding-right: 32px;
padding-top: 24px;
display: flex;
flex-direction: column;
`;
export const ActionButtonsTitle = styled.h3`
color: rgba(${props => props.theme.colors.text.primary});
font-size: 12px;
font-weight: 500;
letter-spacing: 0.04em;
`;
export const ActionButton = styled(Button)`
margin-top: 8px;
margin-left: -10px;
padding: 8px 16px;
background: rgba(${props => props.theme.colors.bg.primary}, 0.5);
text-align: left;
transition: transform 0.2s ease;
& span {
justify-content: flex-start;
}
&:hover {
box-shadow: none;
transform: translateX(4px);
background: rgba(${props => props.theme.colors.bg.primary}, 0.75);
}
`;
export const HeaderRight = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;
export const HeaderActionIcon = styled.div`
padding: 4px 4px 4px 4px;
margin: 0 4px 0 4px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
svg {
fill: rgba(${props => props.theme.colors.text.primary}, 0.75);
}
&:hover svg {
fill: rgba(${props => props.theme.colors.primary});
}
`;
export const EditorContainer = styled.div`
margin-left: 32px;
margin-right: 32px;
padding: 9px 8px 7px 8px;
border-color: transparent;
border-radius: 6px;
border-width: 1px;
border-style: solid;
outline: 0;
background: none;
ul { ul {
margin: 8px 0; list-style-type: disc;
} }
ul > li { ul.checkbox_list input[type='checkbox'] {
margin: 8px 8px 8px 24px; border-radius: 6px;
list-style: disc; border-width: 1px;
} border-style: solid;
border-color: #414561;
p a {
color: rgba(${props => props.theme.colors.primary});
} }
`; `;
export const TaskDetailsControls = styled.div` export const InnerContentContainer = styled.div`
clear: both; overflow: auto;
margin-top: 8px; position: relative;
`;
export const DescriptionContainer = styled.div`
position: relative;
display: flex; display: flex;
flex-direction: column;
`; `;
export const ConfirmSave = styled(Button)` export const DescriptionActionButton = styled(Button)`
padding: 6px 12px; padding: 8px 16px;
border-radius: 3px;
margin-right: 6px;
`; `;
export const CancelEdit = styled.div` export const Labels = styled.div`
display: flex; display: flex;
align-items: center; padding-left: 9px;
justify-content: center; margin: 0 0 8px 0;
height: 32px;
width: 32px;
cursor: pointer;
`; `;
export const TaskDetailSectionTitle = styled.div` export const MetaDetail = styled.div`
text-transform: uppercase;
color: #c2c6dc;
font-size: 12.5px;
font-weight: 600;
margin: 24px 0px 5px;
`;
export const TaskDetailAssignees = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
`;
export const TaskDetailAssignee = styled.div`
&:hover {
opacity: 0.8;
}
margin-right: 4px;
`;
export const ProfileIcon = styled.div`
width: 32px;
height: 32px;
border-radius: 9999px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 700;
background: rgb(115, 103, 240);
cursor: pointer;
`;
export const TaskDetailsAddMemberIcon = styled.div`
float: left;
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 100%;
background: ${mixin.darken('#262c49', 0.15)};
cursor: pointer;
&:hover {
opacity: 0.8;
}
`;
export const TaskDetailLabels = styled.div`
display: flex;
align-items: center;
flex-wrap: wrap;
`;
export const TaskDetailLabel = styled.div<{ color: string }>`
&:hover {
opacity: 0.8;
}
background-color: ${props => props.color};
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
border-radius: 3px;
box-sizing: border-box;
display: block; display: block;
float: left; float: left;
font-weight: 600; margin: 0 16px 8px 0;
height: 32px; max-width: 100%;
line-height: 32px;
margin: 0 4px 4px 0;
min-width: 40px;
padding: 0 12px;
width: auto;
`; `;
export const MetaDetailTitle = styled.h3`
color: rgba(${props => props.theme.colors.text.primary});
font-size: 12px;
font-weight: 500;
letter-spacing: 0.04em;
margin-top: 16px;
text-transform: uppercase;
display: block;
line-height: 20px;
margin: 0 8px 4px 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
`;
export const MetaDetailContent = styled.div`
display: flex;
`;
export const TaskDetailsAddLabel = styled.div` export const TaskDetailsAddLabel = styled.div`
border-radius: 3px; border-radius: 3px;
background: ${mixin.darken('#262c49', 0.15)}; background: ${mixin.darken('#262c49', 0.15)};
@ -307,90 +384,204 @@ export const TaskDetailsAddLabelIcon = styled.div`
} }
`; `;
export const NoDueDateLabel = styled.span` export const ChecklistSection = styled.div`
color: rgb(137, 147, 164); margin-top: 8px;
font-size: 14px; padding: 0 24px;
cursor: pointer;
`; `;
export const UnassignedLabel = styled.div` export const TaskDetailLabel = styled.div<{ color: string }>`
color: rgb(137, 147, 164); &:hover {
font-size: 14px; opacity: 0.8;
}
background-color: ${props => props.color};
color: #fff;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
border-radius: 3px;
box-sizing: border-box;
display: block;
float: left;
font-weight: 600;
height: 32px; height: 32px;
line-height: 32px;
margin: 0 4px 0 0;
min-width: 40px;
padding: 0 12px;
width: auto;
`; `;
export const ActionButtons = styled.div` export const MemberList = styled.div`
display: flex;
align-items: center;
padding: 4px 16px 4px 8px;
margin-left: -1px;
`;
export const TaskMember = styled(TaskAssignee)`
margin-right: 4px;
`;
export const ActionButtonIcon = styled.div``;
export const EditorActions = styled.div`
display: flex;
align-items: center;
margin-left: 32px;
margin-right: 32px;
padding: 9px 8px 7px 8px;
`;
export const CancelIcon = styled.div`
width: 32px;
height: 32p;
display: flex;
align-items: center;
justify-content: center;
margin-left: 4px;
`;
export const TabBarSection = styled.div`
margin-top: 2px;
padding-left: 23px;
display: flex;
text-transform: uppercase;
min-height: 35px;
border-bottom: 1px solid #414561;
`;
export const TabBarItem = styled.div`
box-shadow: inset 0 -2px rgba(216, 93, 216);
padding: 12px 7px 14px 7px;
margin-bottom: -1px;
margin-right: 36px;
color: rgba(${props => props.theme.colors.text.primary});
`;
export const CommentContainer = styled.div`
flex: 0 0 auto;
margin-top: auto;
padding: 15px 26px;
background: #1f243e;
`;
export const CommentInnerWrapper = styled.div`
display: flex;
position: relative;
`;
export const CommentEditorContainer = styled.div`
flex: 1;
border-radius: 6px;
border: 1px solid #414561;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
`; `;
export const CommentProfile = styled(TaskAssignee)`
margin-right: 8px;
position: relative;
top: 0;
padding-top: 3px;
align-items: normal;
`;
export const ActionButtonsTitle = styled.h3` export const CommentTextArea = styled(TextareaAutosize)`
width: 100%;
line-height: 28px;
padding: 4px 6px;
border-radius: 6px;
color: rgba(${props => props.theme.colors.text.primary}); color: rgba(${props => props.theme.colors.text.primary});
font-size: 12px; background: #1f243e;
font-weight: 500; border: none;
letter-spacing: 0.04em; transition: max-height 200ms, height 200ms, min-height 200ms;
`; min-height: 36px;
max-height: 36px;
export const ActionButton = styled(Button)` &:not(:focus) {
margin-top: 8px; height: 36px;
padding: 6px 12px;
background: rgba(${props => props.theme.colors.bg.primary}, 0.4);
text-align: left;
&:hover {
box-shadow: none;
background: rgba(${props => props.theme.colors.bg.primary}, 0.6);
} }
`; &:focus {
min-height: 80px;
export const MetaDetails = styled.div` max-height: none;
margin-top: 8px;
display: flex;
`;
export const TaskDueDateButton = styled(Button)`
height: 32px;
padding: 6px 12px;
background: rgba(${props => props.theme.colors.bg.primary}, 0.4);
&:hover {
box-shadow: none;
background: rgba(${props => props.theme.colors.bg.primary}, 0.6);
}
`;
export const MetaDetail = styled.div`
display: block;
float: left;
margin: 0 16px 8px 0;
max-width: 100%;
`;
export const TaskDetailsSection = styled.div`
display: block;
`;
export const MetaDetailTitle = styled.h3`
color: rgba(${props => props.theme.colors.text.primary});
font-size: 12px;
font-weight: 500;
letter-spacing: 0.04em;
margin-top: 16px;
text-transform: uppercase;
display: block;
line-height: 20px; line-height: 20px;
margin: 0 8px 4px 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
`;
export const TaskMember = styled(TaskAssignee)``;
export const MetaDetailContent = styled.div`
display: flex;
& ${TaskMember} {
margin-right: 4px;
} }
`; `;
export const CommentEditorActions = styled.div<{ visible: boolean }>`
display: ${props => (props.visible ? 'flex' : 'none')};
align-items: center;
padding: 5px 5px 5px 9px;
border-top: 1px solid #414561;
`;
export const CommentEditorActionIcon = styled.div`
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
`;
export const CommentEditorSaveButton = styled(Button)`
margin-left: auto;
padding: 8px 16px;
`;
export const ActivitySection = styled.div`
overflow-x: hidden;
padding: 8px 26px;
`;
export const ActivityItem = styled.div`
padding: 8px 0;
position: relative;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
`;
export const ActivityItemHeader = styled.div`
display: flex;
`;
export const ActivityItemHeaderUser = styled(TaskAssignee)`
margin-right: 4px;
`;
export const ActivityItemHeaderTitle = styled.div`
margin-left: 4px;
line-height: 18px;
display: flex;
align-items: center;
`;
export const ActivityItemHeaderTitleName = styled.span`
color: rgba(${props => props.theme.colors.text.primary});
font-weight: 500;
`;
export const ActivityItemTimestamp = styled.span<{ margin: number }>`
font-size: 12px;
color: rgba(${props => props.theme.colors.text.primary}, 0.65);
margin-left: ${props => props.margin}px;
`;
export const ActivityItemDetails = styled.div`
margin-left: 32px;
`;
export const ActivityItemComment = styled.div`
display: inline-flex;
border-radius: 3px;
${mixin.boxShadowCard}
position: relative;
color: rgba(${props => props.theme.colors.text.primary});
padding: 8px 12px;
margin: 4px 0;
background-color: ${mixin.darken('#262c49', 0.1)};
`;
export const ActivityItemLog = styled.span`
margin-left: 2px;
color: rgba(${props => props.theme.colors.text.primary});
`;

View File

@ -1,70 +1,76 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef } from 'react';
import { Bin, Cross, Plus } from 'shared/icons'; import {
import useOnOutsideClick from 'shared/hooks/onOutsideClick'; Plus,
import ReactMarkdown from 'react-markdown'; User,
Trash,
Paperclip,
Clone,
Share,
Tags,
Checkmark,
CheckSquareOutline,
At,
Smile,
} from 'shared/icons';
import Editor from 'rich-markdown-editor';
import dark from 'shared/utils/editorTheme';
import styled from 'styled-components'; import styled from 'styled-components';
import { import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
isPositionChanged,
getSortedDraggables,
getNewDraggablePosition,
getAfterDropDraggableList,
} from 'shared/utils/draggables';
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
import TaskAssignee from 'shared/components/TaskAssignee';
import moment from 'moment'; import moment from 'moment';
import Task from 'shared/icons/Task';
import { import {
TaskMember, TaskDetailLabel,
NoDueDateLabel, CommentContainer,
TaskDueDateButton, MetaDetailContent,
UnassignedLabel,
TaskGroupLabel,
TaskGroupLabelName,
TaskDetailsSection,
TaskActions,
TaskDetailsAddLabel,
TaskDetailsAddLabelIcon, TaskDetailsAddLabelIcon,
TaskAction,
TaskMeta,
ActionButtons,
ActionButton, ActionButton,
ActionButtonsTitle, AssignUserIcon,
TaskHeader, AssignUserLabel,
ProfileIcon, AssignUsersButton,
TaskDetailsContent, AssignedUsersSection,
TaskDetailsWrapper, DueDateTitle,
TaskDetailsSidebar, Container,
LeftSidebar,
ContentContainer,
LeftSidebarContent,
LeftSidebarSection,
SidebarTitle,
SidebarButton,
SidebarButtonText,
MarkCompleteButton,
HeaderContainer,
HeaderLeft,
HeaderInnerContainer,
TaskDetailsTitleWrapper, TaskDetailsTitleWrapper,
TaskDetailsTitle, TaskDetailsTitle,
TaskDetailsLabel, ExtraActionsSection,
TaskDetailsAddDetailsButton, HeaderRight,
TaskDetailsEditor, HeaderActionIcon,
TaskDetailsEditorWrapper, EditorContainer,
TaskDetailsMarkdown, InnerContentContainer,
TaskDetailsControls, DescriptionContainer,
ConfirmSave, Labels,
CancelEdit, ChecklistSection,
TaskDetailSectionTitle, MemberList,
TaskDetailLabel, TaskMember,
TaskDetailLabels, TabBarSection,
TaskDetailAssignee, TabBarItem,
TaskDetailAssignees, CommentTextArea,
TaskDetailsAddMemberIcon, CommentEditorContainer,
MetaDetails, CommentEditorActions,
MetaDetail, CommentEditorActionIcon,
MetaDetailTitle, CommentEditorSaveButton,
MetaDetailContent, CommentProfile,
CommentInnerWrapper,
ActivitySection,
} from './Styles'; } from './Styles';
import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist'; import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
import onDragEnd from './onDragEnd';
const ChecklistContainer = styled.div``; const ChecklistContainer = styled.div``;
type TaskContentProps = {
onEditContent: () => void;
description: string;
};
type TaskLabelProps = { type TaskLabelProps = {
label: TaskLabel; label: TaskLabel;
onClick: ($target: React.RefObject<HTMLElement>) => void; onClick: ($target: React.RefObject<HTMLElement>) => void;
@ -85,62 +91,15 @@ const TaskLabelItem: React.FC<TaskLabelProps> = ({ label, onClick }) => {
); );
}; };
const TaskContent: React.FC<TaskContentProps> = ({ description, onEditContent }) => {
return description === '' ? (
<TaskDetailsAddDetailsButton onClick={onEditContent}>Add a more detailed description</TaskDetailsAddDetailsButton>
) : (
<TaskDetailsMarkdown onClick={onEditContent}>
<ReactMarkdown source={description} />
</TaskDetailsMarkdown>
);
};
type DetailsEditorProps = { type DetailsEditorProps = {
description: string; description: string;
onTaskDescriptionChange: (newDescription: string) => void; onTaskDescriptionChange: (newDescription: string) => void;
onCancel: () => void; onCancel: () => void;
}; };
const DetailsEditor: React.FC<DetailsEditorProps> = ({
description: initialDescription,
onTaskDescriptionChange,
onCancel,
}) => {
const [description, setDescription] = useState(initialDescription);
const $editorWrapperRef = useRef<HTMLDivElement>(null);
const $editorRef = useRef<HTMLTextAreaElement>(null);
const handleOutsideClick = () => {
onTaskDescriptionChange(description);
};
useEffect(() => {
if ($editorRef && $editorRef.current) {
$editorRef.current.focus();
$editorRef.current.select();
}
}, []);
useOnOutsideClick($editorWrapperRef, true, handleOutsideClick, null);
return (
<TaskDetailsEditorWrapper ref={$editorWrapperRef}>
<TaskDetailsEditor
ref={$editorRef}
value={description}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setDescription(e.currentTarget.value)}
/>
<TaskDetailsControls>
<ConfirmSave variant="relief" onClick={handleOutsideClick}>
Save
</ConfirmSave>
<CancelEdit onClick={onCancel}>
<Plus size={16} color="#c2c6dc" />
</CancelEdit>
</TaskDetailsControls>
</TaskDetailsEditorWrapper>
);
};
type TaskDetailsProps = { type TaskDetailsProps = {
task: Task; task: Task;
me?: TaskUser | null;
onTaskNameChange: (task: Task, newName: string) => void; onTaskNameChange: (task: Task, newName: string) => void;
onTaskDescriptionChange: (task: Task, newDescription: string) => void; onTaskDescriptionChange: (task: Task, newDescription: string) => void;
onDeleteTask: (task: Task) => void; onDeleteTask: (task: Task) => void;
@ -162,6 +121,7 @@ type TaskDetailsProps = {
}; };
const TaskDetails: React.FC<TaskDetailsProps> = ({ const TaskDetails: React.FC<TaskDetailsProps> = ({
me,
task, task,
onDeleteChecklist, onDeleteChecklist,
onTaskNameChange, onTaskNameChange,
@ -182,163 +142,152 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
onToggleChecklistItem, onToggleChecklistItem,
onMemberProfile, onMemberProfile,
}) => { }) => {
const [editorOpen, setEditorOpen] = useState(false);
const [description, setDescription] = useState(task.description ?? '');
const [taskName, setTaskName] = useState(task.name); const [taskName, setTaskName] = useState(task.name);
const handleClick = () => { const [editTaskDescription, setEditTaskDescription] = useState(() => {
setEditorOpen(!editorOpen); if (task.description) {
}; if (task.description.trim() === '' || task.description.trim() === '\\') {
const handleDeleteTask = () => { return true;
onDeleteTask(task);
};
const $title = useRef<HTMLTextAreaElement>(null);
const onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
onTaskNameChange(task, taskName);
if ($title && $title.current) {
$title.current.blur();
} }
return false;
} }
}; return true;
const $unassignedRef = useRef<HTMLDivElement>(null); });
const $addMemberRef = useRef<HTMLDivElement>(null); const [saveTimeout, setSaveTimeout] = useState<any>(null);
const onUnassignedClick = () => { const [showCommentActions, setShowCommentActions] = useState(false);
onOpenAddMemberPopup(task, $unassignedRef); const taskDescriptionRef = useRef(task.description ?? '');
}; const $noMemberBtn = useRef<HTMLDivElement>(null);
const onAddMember = ($target: React.RefObject<HTMLElement>) => { const $addMemberBtn = useRef<HTMLDivElement>(null);
onOpenAddMemberPopup(task, $target); const $dueDateBtn = useRef<HTMLDivElement>(null);
};
const onAddChecklist = ($target: React.RefObject<HTMLElement>) => {
onOpenAddChecklistPopup(task, $target);
};
const $dueDateLabel = useRef<HTMLDivElement>(null);
const $addLabelRef = useRef<HTMLDivElement>(null);
const onAddLabel = ($target: React.RefObject<HTMLElement>) => { const saveDescription = () => {
onOpenAddLabelPopup(task, $target); onTaskDescriptionChange(task, taskDescriptionRef.current);
}; };
const onDragEnd = ({ draggableId, source, destination, type }: DropResult) => {
if (typeof destination === 'undefined') return;
if (!isPositionChanged(source, destination)) return;
const isChecklist = type === 'checklist';
const isSameChecklist = destination.droppableId === source.droppableId;
let droppedDraggable: DraggableElement | null = null;
let beforeDropDraggables: Array<DraggableElement> | null = null;
if (!task.checklists) return;
if (isChecklist) {
const droppedGroup = task.checklists.find(taskGroup => taskGroup.id === draggableId);
if (droppedGroup) {
droppedDraggable = {
id: draggableId,
position: droppedGroup.position,
};
beforeDropDraggables = getSortedDraggables(
task.checklists.map(checklist => {
return { id: checklist.id, position: checklist.position };
}),
);
if (droppedDraggable === null || beforeDropDraggables === null) {
throw new Error('before drop draggables is null');
}
const afterDropDraggables = getAfterDropDraggableList(
beforeDropDraggables,
droppedDraggable,
isChecklist,
isSameChecklist,
destination,
);
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
onChecklistDrop({ ...droppedGroup, position: newPosition });
} else {
throw new Error('task group can not be found');
}
} else {
const targetChecklist = task.checklists.findIndex(
checklist => checklist.items.findIndex(item => item.id === draggableId) !== -1,
);
const droppedChecklistItem = task.checklists[targetChecklist].items.find(item => item.id === draggableId);
if (droppedChecklistItem) {
droppedDraggable = {
id: draggableId,
position: droppedChecklistItem.position,
};
beforeDropDraggables = getSortedDraggables(
task.checklists[targetChecklist].items.map(item => {
return { id: item.id, position: item.position };
}),
);
if (droppedDraggable === null || beforeDropDraggables === null) {
throw new Error('before drop draggables is null');
}
const afterDropDraggables = getAfterDropDraggableList(
beforeDropDraggables,
droppedDraggable,
isChecklist,
isSameChecklist,
destination,
);
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
const newItem = {
...droppedChecklistItem,
position: newPosition,
};
onChecklistItemDrop(droppedChecklistItem.taskChecklistID, destination.droppableId, newItem);
}
}
};
return ( return (
<> <Container>
<TaskActions> <LeftSidebar>
<TaskAction onClick={handleDeleteTask}> <LeftSidebarContent>
<Bin size={20} color="#c2c6dc" /> <LeftSidebarSection>
</TaskAction> <SidebarTitle>TASK GROUP</SidebarTitle>
<TaskAction onClick={onCloseModal}> <SidebarButton>
<Cross width={16} height={16} /> <SidebarButtonText>Release 0.1.0</SidebarButtonText>
</TaskAction> </SidebarButton>
</TaskActions> <DueDateTitle>DUE DATE</DueDateTitle>
<TaskHeader> <SidebarButton
ref={$dueDateBtn}
onClick={() => {
onOpenDueDatePopop(task, $dueDateBtn);
}}
>
{task.dueDate ? (
<SidebarButtonText>{moment(task.dueDate).format('MMM D [at] h:mm A')}</SidebarButtonText>
) : (
<SidebarButtonText>No due date</SidebarButtonText>
)}
</SidebarButton>
</LeftSidebarSection>
<AssignedUsersSection>
<DueDateTitle>MEMBERS</DueDateTitle>
{task.assigned && task.assigned.length !== 0 ? (
<MemberList>
{task.assigned.map(m => (
<TaskMember
key={m.id}
member={m}
size={32}
onMemberProfile={$target => {
onMemberProfile($target, m.id);
}}
/>
))}
<AssignUserIcon
ref={$addMemberBtn}
onClick={() => {
onOpenAddMemberPopup(task, $addMemberBtn);
}}
>
<Plus width={16} height={16} />
</AssignUserIcon>
</MemberList>
) : (
<AssignUsersButton
ref={$noMemberBtn}
onClick={() => {
onOpenAddMemberPopup(task, $noMemberBtn);
}}
>
<AssignUserIcon>
<User width={16} height={16} />
</AssignUserIcon>
<AssignUserLabel>No members</AssignUserLabel>
</AssignUsersButton>
)}
</AssignedUsersSection>
<ExtraActionsSection>
<DueDateTitle>ACTIONS</DueDateTitle>
<ActionButton
onClick={$target => {
onOpenAddLabelPopup(task, $target);
}}
icon={<Tags width={12} height={12} />}
>
Labels
</ActionButton>
<ActionButton
onClick={$target => {
onOpenAddChecklistPopup(task, $target);
}}
icon={<CheckSquareOutline width={12} height={12} />}
>
Checklist
</ActionButton>
<ActionButton>Cover</ActionButton>
</ExtraActionsSection>
</LeftSidebarContent>
</LeftSidebar>
<ContentContainer>
<HeaderContainer>
<HeaderInnerContainer>
<HeaderLeft>
<MarkCompleteButton
invert={task.complete ?? false}
onClick={() => {
onToggleTaskComplete(task);
}}
>
<Checkmark width={8} height={8} />
<span>{task.complete ? 'Completed' : 'Mark complete'}</span>
</MarkCompleteButton>
</HeaderLeft>
<HeaderRight>
<HeaderActionIcon>
<Paperclip width={16} height={16} />
</HeaderActionIcon>
<HeaderActionIcon>
<Clone width={16} height={16} />
</HeaderActionIcon>
<HeaderActionIcon>
<Share width={16} height={16} />
</HeaderActionIcon>
<HeaderActionIcon onClick={() => onDeleteTask(task)}>
<Trash width={16} height={16} />
</HeaderActionIcon>
</HeaderRight>
</HeaderInnerContainer>
<TaskDetailsTitleWrapper> <TaskDetailsTitleWrapper>
<TaskDetailsTitle <TaskDetailsTitle
ref={$title}
value={taskName} value={taskName}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setTaskName(e.currentTarget.value)} onChange={e => {
onKeyDown={onKeyDown} setTaskName(e.currentTarget.value);
}}
onBlur={() => {
if (taskName !== task.name) {
onTaskNameChange(task, taskName);
}
}}
/> />
</TaskDetailsTitleWrapper> </TaskDetailsTitleWrapper>
<TaskMeta> <Labels>
{task.taskGroup.name && (
<TaskGroupLabel>
{`in list ${(<TaskGroupLabelName>{task.taskGroup.name}</TaskGroupLabelName>)}`}
</TaskGroupLabel>
)}
</TaskMeta>
</TaskHeader>
<TaskDetailsWrapper>
<TaskDetailsContent>
<MetaDetails>
{task.assigned && task.assigned.length !== 0 && (
<MetaDetail>
<MetaDetailTitle>MEMBERS</MetaDetailTitle>
<MetaDetailContent>
{task.assigned &&
task.assigned.map(member => (
<TaskMember key={member.id} size={32} member={member} onMemberProfile={onMemberProfile} />
))}
<TaskDetailsAddMemberIcon ref={$addMemberRef} onClick={() => onAddMember($addMemberRef)}>
<Plus size={16} color="#c2c6dc" />
</TaskDetailsAddMemberIcon>
</MetaDetailContent>
</MetaDetail>
)}
{task.labels.length !== 0 && ( {task.labels.length !== 0 && (
<MetaDetail>
<MetaDetailTitle>LABELS</MetaDetailTitle>
<MetaDetailContent> <MetaDetailContent>
{task.labels.map(label => { {task.labels.map(label => {
return ( return (
@ -351,40 +300,40 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
/> />
); );
})} })}
<TaskDetailsAddLabelIcon ref={$addLabelRef} onClick={() => onAddLabel($addLabelRef)}> <TaskDetailsAddLabelIcon>
<Plus size={16} color="#c2c6dc" /> <Plus width={12} height={12} />
</TaskDetailsAddLabelIcon> </TaskDetailsAddLabelIcon>
</MetaDetailContent> </MetaDetailContent>
</MetaDetail>
)} )}
{task.dueDate && ( </Labels>
<MetaDetail> </HeaderContainer>
<MetaDetailTitle>DUE DATE</MetaDetailTitle> <InnerContentContainer>
<MetaDetailContent> <DescriptionContainer>
<TaskDueDateButton>{moment(task.dueDate).format('MMM D [at] h:mm A')}</TaskDueDateButton> <EditorContainer
</MetaDetailContent> onClick={e => {
</MetaDetail> if (!editTaskDescription) {
)} setEditTaskDescription(true);
</MetaDetails> }
<TaskDetailsSection>
<TaskDetailsLabel>Description</TaskDetailsLabel>
{editorOpen ? (
<DetailsEditor
description={description}
onTaskDescriptionChange={newDescription => {
setEditorOpen(false);
setDescription(newDescription);
onTaskDescriptionChange(task, newDescription);
}} }}
onCancel={() => { >
setEditorOpen(false); <Editor
defaultValue={task.description ?? ''}
theme={dark}
readOnly={!editTaskDescription}
autoFocus
onChange={value => {
setSaveTimeout(() => {
clearTimeout(saveTimeout);
return setTimeout(saveDescription, 2000);
});
const text = value();
taskDescriptionRef.current = text;
}} }}
/> />
) : ( </EditorContainer>
<TaskContent description={description} onEditContent={handleClick} /> </DescriptionContainer>
)} <ChecklistSection>
<DragDropContext onDragEnd={onDragEnd}> <DragDropContext onDragEnd={result => onDragEnd(result, task, onChecklistDrop, onChecklistItemDrop)}>
<Droppable direction="vertical" type="checklist" droppableId="root"> <Droppable direction="vertical" type="checklist" droppableId="root">
{dropProvided => ( {dropProvided => (
<ChecklistContainer {...dropProvided.droppableProps} ref={dropProvided.innerRef}> <ChecklistContainer {...dropProvided.droppableProps} ref={dropProvided.innerRef}>
@ -463,32 +412,54 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
)} )}
</Droppable> </Droppable>
</DragDropContext> </DragDropContext>
</TaskDetailsSection> </ChecklistSection>
</TaskDetailsContent> <TabBarSection>
<TaskDetailsSidebar> <TabBarItem>Activity</TabBarItem>
<ActionButtons> </TabBarSection>
<ActionButtonsTitle>ADD TO CARD</ActionButtonsTitle> <ActivitySection />
<ActionButton justifyTextContent="flex-start" onClick={() => onToggleTaskComplete(task)}> </InnerContentContainer>
{task.complete ? 'Mark Incomplete' : 'Mark Complete'} <CommentContainer>
</ActionButton> {me && (
<ActionButton justifyTextContent="flex-start" onClick={$target => onAddMember($target)}> <CommentInnerWrapper>
Members <CommentProfile
</ActionButton> member={me}
<ActionButton justifyTextContent="flex-start" onClick={$target => onAddLabel($target)}> size={32}
Labels onMemberProfile={$target => {
</ActionButton> onMemberProfile($target, me.id);
<ActionButton justifyTextContent="flex-start" onClick={$target => onAddChecklist($target)}> }}
Checklist />
</ActionButton> <CommentEditorContainer>
<ActionButton justifyTextContent="flex-start" onClick={$target => onOpenDueDatePopop(task, $target)}> <CommentTextArea
Due Date disabled
</ActionButton> placeholder="Write a comment..."
<ActionButton justifyTextContent="flex-start">Attachment</ActionButton> onFocus={() => {
<ActionButton justifyTextContent="flex-start">Cover</ActionButton> setShowCommentActions(true);
</ActionButtons> }}
</TaskDetailsSidebar> onBlur={() => {
</TaskDetailsWrapper> setShowCommentActions(false);
</> }}
/>
<CommentEditorActions visible={showCommentActions}>
<CommentEditorActionIcon>
<Paperclip width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorActionIcon>
<At width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorActionIcon>
<Smile width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorActionIcon>
<Task width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorSaveButton>Save</CommentEditorSaveButton>
</CommentEditorActions>
</CommentEditorContainer>
</CommentInnerWrapper>
)}
</CommentContainer>
</ContentContainer>
</Container>
); );
}; };

View File

@ -0,0 +1,90 @@
import {
getSortedDraggables,
isPositionChanged,
getNewDraggablePosition,
getAfterDropDraggableList,
} from 'shared/utils/draggables';
import { DropResult } from 'react-beautiful-dnd';
type OnChecklistDropFn = (checklist: TaskChecklist) => void;
type OnChecklistItemDropFn = (prevChecklistID: string, checklistID: string, checklistItem: TaskChecklistItem) => void;
const onDragEnd = (
{ draggableId, source, destination, type }: DropResult,
task: Task,
onChecklistDrop: OnChecklistDropFn,
onChecklistItemDrop: OnChecklistItemDropFn,
) => {
if (typeof destination === 'undefined') return;
if (!isPositionChanged(source, destination)) return;
const isChecklist = type === 'checklist';
const isSameChecklist = destination.droppableId === source.droppableId;
let droppedDraggable: DraggableElement | null = null;
let beforeDropDraggables: Array<DraggableElement> | null = null;
if (!task.checklists) return;
if (isChecklist) {
const droppedGroup = task.checklists.find(taskGroup => taskGroup.id === draggableId);
if (droppedGroup) {
droppedDraggable = {
id: draggableId,
position: droppedGroup.position,
};
beforeDropDraggables = getSortedDraggables(
task.checklists.map(checklist => {
return { id: checklist.id, position: checklist.position };
}),
);
if (droppedDraggable === null || beforeDropDraggables === null) {
throw new Error('before drop draggables is null');
}
const afterDropDraggables = getAfterDropDraggableList(
beforeDropDraggables,
droppedDraggable,
isChecklist,
isSameChecklist,
destination,
);
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
onChecklistDrop({ ...droppedGroup, position: newPosition });
} else {
throw new Error('task group can not be found');
}
} else {
const targetChecklist = task.checklists.findIndex(
checklist => checklist.items.findIndex(item => item.id === draggableId) !== -1,
);
const droppedChecklistItem = task.checklists[targetChecklist].items.find(item => item.id === draggableId);
if (droppedChecklistItem) {
droppedDraggable = {
id: draggableId,
position: droppedChecklistItem.position,
};
beforeDropDraggables = getSortedDraggables(
task.checklists[targetChecklist].items.map(item => {
return { id: item.id, position: item.position };
}),
);
if (droppedDraggable === null || beforeDropDraggables === null) {
throw new Error('before drop draggables is null');
}
const afterDropDraggables = getAfterDropDraggableList(
beforeDropDraggables,
droppedDraggable,
isChecklist,
isSameChecklist,
destination,
);
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
const newItem = {
...droppedChecklistItem,
position: newPosition,
};
onChecklistItemDrop(droppedChecklistItem.taskChecklistID, destination.droppableId, newItem);
}
}
};
export default onDragEnd;

View File

@ -1185,6 +1185,16 @@ export type FindTaskQuery = (
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'> & Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
) } ) }
)> } )> }
), me: (
{ __typename?: 'MePayload' }
& { user: (
{ __typename?: 'UserAccount' }
& Pick<UserAccount, 'id' | 'fullName'>
& { profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'initials' | 'bgColor' | 'url'>
) }
) }
) } ) }
); );
@ -2513,6 +2523,17 @@ export const FindTaskDocument = gql`
} }
} }
} }
me {
user {
id
fullName
profileIcon {
initials
bgColor
url
}
}
}
} }
`; `;

View File

@ -52,4 +52,15 @@ query findTask($taskID: UUID!) {
} }
} }
} }
me {
user {
id
fullName
profileIcon {
initials
bgColor
url
}
}
}
} }

View File

@ -0,0 +1,14 @@
import { useLayoutEffect, useState } from 'react';
export default function useWindowSize() {
const [size, setSize] = useState([0, 0]);
useLayoutEffect(() => {
function updateSize() {
setSize([window.innerWidth, window.innerHeight]);
}
window.addEventListener('resize', updateSize);
updateSize();
return () => window.removeEventListener('resize', updateSize);
}, []);
return size;
}

View File

@ -0,0 +1,12 @@
import React from 'react';
import Icon, { IconProps } from './Icon';
const At: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
return (
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
<path d="M256 8C118.941 8 8 118.919 8 256c0 137.059 110.919 248 248 248 48.154 0 95.342-14.14 135.408-40.223 12.005-7.815 14.625-24.288 5.552-35.372l-10.177-12.433c-7.671-9.371-21.179-11.667-31.373-5.129C325.92 429.757 291.314 440 256 440c-101.458 0-184-82.542-184-184S154.542 72 256 72c100.139 0 184 57.619 184 160 0 38.786-21.093 79.742-58.17 83.693-17.349-.454-16.91-12.857-13.476-30.024l23.433-121.11C394.653 149.75 383.308 136 368.225 136h-44.981a13.518 13.518 0 0 0-13.432 11.993l-.01.092c-14.697-17.901-40.448-21.775-59.971-21.775-74.58 0-137.831 62.234-137.831 151.46 0 65.303 36.785 105.87 96 105.87 26.984 0 57.369-15.637 74.991-38.333 9.522 34.104 40.613 34.103 70.71 34.103C462.609 379.41 504 307.798 504 232 504 95.653 394.023 8 256 8zm-21.68 304.43c-22.249 0-36.07-15.623-36.07-40.771 0-44.993 30.779-72.729 58.63-72.729 22.292 0 35.601 15.241 35.601 40.77 0 45.061-33.875 72.73-58.161 72.73z" />
</Icon>
);
};
export default At;

View File

@ -0,0 +1,12 @@
import React from 'react';
import Icon, { IconProps } from './Icon';
const Clone: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
return (
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
<path d="M464 0c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48H176c-26.51 0-48-21.49-48-48V48c0-26.51 21.49-48 48-48h288M176 416c-44.112 0-80-35.888-80-80V128H48c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48v-48H176z" />
</Icon>
);
};
export default Clone;

View File

@ -0,0 +1,12 @@
import React from 'react';
import Icon, { IconProps } from './Icon';
const Paperclip: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
return (
<Icon width={width} height={height} className={className} viewBox="0 0 448 512">
<path d="M43.246 466.142c-58.43-60.289-57.341-157.511 1.386-217.581L254.392 34c44.316-45.332 116.351-45.336 160.671 0 43.89 44.894 43.943 117.329 0 162.276L232.214 383.128c-29.855 30.537-78.633 30.111-107.982-.998-28.275-29.97-27.368-77.473 1.452-106.953l143.743-146.835c6.182-6.314 16.312-6.422 22.626-.241l22.861 22.379c6.315 6.182 6.422 16.312.241 22.626L171.427 319.927c-4.932 5.045-5.236 13.428-.648 18.292 4.372 4.634 11.245 4.711 15.688.165l182.849-186.851c19.613-20.062 19.613-52.725-.011-72.798-19.189-19.627-49.957-19.637-69.154 0L90.39 293.295c-34.763 35.56-35.299 93.12-1.191 128.313 34.01 35.093 88.985 35.137 123.058.286l172.06-175.999c6.177-6.319 16.307-6.433 22.626-.256l22.877 22.364c6.319 6.177 6.434 16.307.256 22.626l-172.06 175.998c-59.576 60.938-155.943 60.216-214.77-.485z" />
</Icon>
);
};
export default Paperclip;

View File

@ -1,21 +1,12 @@
import React from 'react'; import React from 'react';
import Icon, { IconProps } from './Icon';
type Props = { const Plus: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
size: number | string;
color: string;
};
const Plus = ({ size, color }: Props) => {
return ( return (
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16"> <Icon width={width} height={height} className={className} viewBox="0 0 448 512">
<path d="M15.5 6h-5.5v-5.5c0-0.276-0.224-0.5-0.5-0.5h-3c-0.276 0-0.5 0.224-0.5 0.5v5.5h-5.5c-0.276 0-0.5 0.224-0.5 0.5v3c0 0.276 0.224 0.5 0.5 0.5h5.5v5.5c0 0.276 0.224 0.5 0.5 0.5h3c0.276 0 0.5-0.224 0.5-0.5v-5.5h5.5c0.276 0 0.5-0.224 0.5-0.5v-3c0-0.276-0.224-0.5-0.5-0.5z" /> <path d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z" />
</svg> </Icon>
); );
}; };
Plus.defaultProps = {
size: 16,
color: '#000',
};
export default Plus; export default Plus;

View File

@ -0,0 +1,12 @@
import React from 'react';
import Icon, { IconProps } from './Icon';
const Share: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
return (
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
<path d="M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z" />
</Icon>
);
};
export default Share;

View File

@ -0,0 +1,12 @@
import React from 'react';
import Icon, { IconProps } from './Icon';
const Smile: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
return (
<Icon width={width} height={height} className={className} viewBox="0 0 496 512">
<path d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z" />
</Icon>
);
};
export default Smile;

View File

@ -0,0 +1,12 @@
import React from 'react';
import Icon, { IconProps } from './Icon';
const Task: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
return (
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
<path d="M139.61 35.5a12 12 0 0 0-17 0L58.93 98.81l-22.7-22.12a12 12 0 0 0-17 0L3.53 92.41a12 12 0 0 0 0 17l47.59 47.4a12.78 12.78 0 0 0 17.61 0l15.59-15.62L156.52 69a12.09 12.09 0 0 0 .09-17zm0 159.19a12 12 0 0 0-17 0l-63.68 63.72-22.7-22.1a12 12 0 0 0-17 0L3.53 252a12 12 0 0 0 0 17L51 316.5a12.77 12.77 0 0 0 17.6 0l15.7-15.69 72.2-72.22a12 12 0 0 0 .09-16.9zM64 368c-26.49 0-48.59 21.5-48.59 48S37.53 464 64 464a48 48 0 0 0 0-96zm432 16H208a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h288a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-320H208a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h288a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 160H208a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h288a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z" />
</Icon>
);
};
export default Task;

View File

@ -1,9 +1,15 @@
import Cross from './Cross'; import Cross from './Cross';
import Cog from './Cog'; import Cog from './Cog';
import At from './At';
import Task from './Task';
import Smile from './Smile';
import Paperclip from './Paperclip';
import Calendar from './Calendar'; import Calendar from './Calendar';
import Clone from './Clone';
import Sort from './Sort'; import Sort from './Sort';
import Filter from './Filter'; import Filter from './Filter';
import DoubleChevronUp from './DoubleChevronUp'; import DoubleChevronUp from './DoubleChevronUp';
import Share from './Share';
import Crown from './Crown'; import Crown from './Crown';
import BarChart from './BarChart'; import BarChart from './BarChart';
import UserPlus from './UserPlus'; import UserPlus from './UserPlus';
@ -69,9 +75,14 @@ export {
Square, Square,
Filter, Filter,
Sort, Sort,
At,
Smile,
DoubleChevronUp, DoubleChevronUp,
UserPlus, UserPlus,
Crown, Crown,
ToggleOn, ToggleOn,
Calendar, Calendar,
Clone,
Paperclip,
Share,
}; };

View File

@ -0,0 +1,86 @@
import theme from 'App/ThemeStyles';
const colors = {
almostBlack: '#181A1B',
lightBlack: '#2F3336',
almostWhite: '#E6E6E6',
white: '#FFF',
white10: 'rgba(255, 255, 255, 0.1)',
black: '#000',
black10: 'rgba(0, 0, 0, 0.1)',
primary: '#1AB6FF',
greyLight: '#F4F7FA',
grey: '#E8EBED',
greyMid: '#C5CCD3',
greyDark: '#DAE1E9',
};
export const base = {
...colors,
fontFamily: "'Droid Sans', sans-serif",
fontFamilyMono: "'SFMono-Regular',Consolas,'Liberation Mono', Menlo, Courier,monospace",
fontWeight: 400,
zIndex: 10000,
link: colors.primary,
placeholder: '#B1BECC',
textSecondary: '#4E5C6E',
textLight: colors.white,
textHighlight: '#b3e7ff',
selected: colors.primary,
codeComment: '#6a737d',
codePunctuation: '#5e6687',
codeNumber: '#d73a49',
codeProperty: '#c08b30',
codeTag: '#3d8fd1',
codeString: '#032f62',
codeSelector: '#6679cc',
codeAttr: '#c76b29',
codeEntity: '#22a2c9',
codeKeyword: '#d73a49',
codeFunction: '#6f42c1',
codeStatement: '#22a2c9',
codePlaceholder: '#3d8fd1',
codeInserted: '#202746',
codeImportant: '#c94922',
blockToolbarBackground: colors.white,
blockToolbarTrigger: colors.greyMid,
blockToolbarTriggerIcon: colors.white,
blockToolbarItem: colors.almostBlack,
blockToolbarText: colors.almostBlack,
blockToolbarHoverBackground: colors.greyLight,
blockToolbarDivider: colors.greyMid,
noticeInfoBackground: '#F5BE31',
noticeInfoText: colors.almostBlack,
noticeTipBackground: '#9E5CF7',
noticeTipText: colors.white,
noticeWarningBackground: '#FF5C80',
noticeWarningText: colors.white,
};
export const dark = {
...base,
background: 'transparent',
text: `rgba(${theme.colors.text.primary})`,
code: `rgba(${theme.colors.text.primary})`,
cursor: `rgba(${theme.colors.text.primary})`,
divider: '#4E5C6E',
placeholder: '#52657A',
toolbarBackground: colors.white,
toolbarInput: colors.black10,
toolbarItem: colors.lightBlack,
tableDivider: colors.lightBlack,
tableSelected: colors.primary,
tableSelectedBackground: '#002333',
quote: colors.greyDark,
codeBackground: colors.black,
codeBorder: colors.lightBlack,
codeString: '#3d8fd1',
horizontalRule: colors.lightBlack,
imageErrorBackground: 'rgba(0, 0, 0, 0.5)',
};
export default dark;

View File

@ -1534,6 +1534,13 @@
dependencies: dependencies:
"@emotion/memoize" "0.7.4" "@emotion/memoize" "0.7.4"
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4": "@emotion/memoize@0.7.4":
version "0.7.4" version "0.7.4"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
@ -3637,13 +3644,6 @@
text-table "^0.2.0" text-table "^0.2.0"
webpack-log "^1.1.2" webpack-log "^1.1.2"
"@welldone-software/why-did-you-render@^4.2.2":
version "4.2.2"
resolved "https://registry.yarnpkg.com/@welldone-software/why-did-you-render/-/why-did-you-render-4.2.2.tgz#720128a4f626997ece1ac455a7b13f9ef10dae0a"
integrity sha512-v08t2WXFQdnxkPodXzbPeho3FwgrlwzjwxasN+8A1LSZnkcxJpYvOF8/z+OySqehC44JT6oPB1KEnBVMrebHdw==
dependencies:
lodash "^4"
"@wry/context@^0.4.0": "@wry/context@^0.4.0":
version "0.4.4" version "0.4.4"
resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.4.4.tgz#e50f5fa1d6cfaabf2977d1fda5ae91717f8815f8" resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.4.4.tgz#e50f5fa1d6cfaabf2977d1fda5ae91717f8815f8"
@ -3760,18 +3760,6 @@ adjust-sourcemap-loader@2.0.0:
object-path "0.11.4" object-path "0.11.4"
regex-parser "2.2.10" regex-parser "2.2.10"
ag-grid-community@^23.2.0:
version "23.2.0"
resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-23.2.0.tgz#889f52e8eb91c167c2ac7477938cbf498a54f67c"
integrity sha512-aG7Ghfu79HeqOCd50GhFSeZUX1Tw9BVUX1VKMuglkAcwYPTQjuYvYT7QVQB5FGzfFjcVq4a1QFfcgdoAcZYJIA==
ag-grid-react@^23.2.0:
version "23.2.0"
resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-23.2.0.tgz#00a54cb4e83c0d35a49c202e5833e3bb20d8cfa8"
integrity sha512-lDGV+WX0Nj5biNOJRSErFehXG+nqkbuXPMS7YJxEDWJLJxtOF0INP5sL6dtxV12j/XHqXa+M2CgQBXZWZq+EWg==
dependencies:
prop-types "^15.6.2"
agent-base@4, agent-base@^4.3.0: agent-base@4, agent-base@^4.3.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
@ -5852,6 +5840,11 @@ compression@^1.7.4:
safe-buffer "5.1.2" safe-buffer "5.1.2"
vary "~1.1.2" vary "~1.1.2"
compute-scroll-into-view@^1.0.14:
version "1.0.14"
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz#80e3ebb25d6aa89f42e533956cb4b16a04cfe759"
integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==
computed-style@~0.1.3: computed-style@~0.1.3:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/computed-style/-/computed-style-0.1.4.tgz#7f344fd8584b2e425bedca4a1afc0e300bb05d74" resolved "https://registry.yarnpkg.com/computed-style/-/computed-style-0.1.4.tgz#7f344fd8584b2e425bedca4a1afc0e300bb05d74"
@ -7003,6 +6996,11 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
entities@~2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
env-ci@^2.1.0: env-ci@^2.1.0:
version "2.6.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-2.6.0.tgz#3fc46537c972b4d3ab5f0b82d07dfc1491297662" resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-2.6.0.tgz#3fc46537c972b4d3ab5f0b82d07dfc1491297662"
@ -10464,6 +10462,13 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
linkify-it@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
dependencies:
uc.micro "^1.0.1"
listr-silent-renderer@^1.1.1: listr-silent-renderer@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
@ -10684,7 +10689,7 @@ lodash.xorby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.xorby/-/lodash.xorby-4.7.0.tgz#9c19a6f9f063a6eb53dd03c1b6871799801463d7" resolved "https://registry.yarnpkg.com/lodash.xorby/-/lodash.xorby-4.7.0.tgz#9c19a6f9f063a6eb53dd03c1b6871799801463d7"
integrity sha1-nBmm+fBjputT3QPBtocXmYAUY9c= integrity sha1-nBmm+fBjputT3QPBtocXmYAUY9c=
lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4, lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5: lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5:
version "4.17.15" version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@ -10840,6 +10845,27 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
markdown-it-container@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-3.0.0.tgz#1d19b06040a020f9a827577bb7dbf67aa5de9a5b"
integrity sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw==
markdown-it-mark@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-mark/-/markdown-it-mark-3.0.0.tgz#27c3e39ef3cc310b2dde5375082c9fa912983cda"
integrity sha512-HqMWeKfMMOu4zBO0emmxsoMWmbf2cPKZY1wP6FsTbKmicFfp5y4L3KXAsNeO1rM6NTJVOrNlLKMPjWzriBGspw==
markdown-it@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
dependencies:
argparse "^1.0.7"
entities "~2.0.0"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3: markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3:
version "6.11.0" version "6.11.0"
resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0" resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0"
@ -11717,6 +11743,11 @@ optionator@^0.8.1, optionator@^0.8.3:
type-check "~0.3.2" type-check "~0.3.2"
word-wrap "~1.2.3" word-wrap "~1.2.3"
orderedmap@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.1.tgz#c618e77611b3b21d0fe3edc92586265e0059c789"
integrity sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==
original@^1.0.0: original@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
@ -11750,6 +11781,11 @@ os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
outline-icons@^1.21.0-3:
version "1.21.0-6"
resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.21.0-6.tgz#bbf63fe6cc88ca2fe391e6ba6cc8b62a948607e3"
integrity sha512-iEVK2zTEZ3sLFLsko/V6z3AEiM2EAjEUyLIOzAT2cqRglIbaIWdyitotKVMb2hWZo66bSvHxA/Rdvv51sw5RhA==
p-defer@^1.0.0: p-defer@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
@ -12966,6 +13002,13 @@ prisma-yml@1.34.10:
scuid "^1.0.2" scuid "^1.0.2"
yaml-ast-parser "^0.0.40" yaml-ast-parser "^0.0.40"
prismjs@^1.19.0:
version "1.21.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3"
integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==
optionalDependencies:
clipboard "^2.0.0"
prismjs@^1.8.4: prismjs@^1.8.4:
version "1.19.0" version "1.19.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.19.0.tgz#713afbd45c3baca4b321569f2df39e17e729d4dc" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.19.0.tgz#713afbd45c3baca4b321569f2df39e17e729d4dc"
@ -13071,6 +13114,129 @@ property-information@^5.0.0, property-information@^5.3.0:
dependencies: dependencies:
xtend "^4.0.0" xtend "^4.0.0"
prosemirror-commands@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.4.tgz#991563e67623acab4f8c510fad1570f8b4693780"
integrity sha512-kj4Qi+8h3EpJtZuuEDwZ9h2/QNGWDsIX/CzjmClxi9GhxWyBUMVUvIFk0mgdqHyX20lLeGmOpc0TLA5aPzgpWg==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-dropcursor@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.3.2.tgz#28738c4ed7102e814d7a8a26d70018523fc7cd6d"
integrity sha512-4c94OUGyobGnwcQI70OXyMhE/9T4aTgjU+CHxkd5c7D+jH/J0mKM/lk+jneFVKt7+E4/M0D9HzRPifu8U28Thw==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
prosemirror-view "^1.1.0"
prosemirror-gapcursor@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.1.5.tgz#0c37fd6cbb1d7c46358c2e7397f8da9a8b5c6246"
integrity sha512-SjbUZq5pgsBDuV3hu8GqgIpZR5eZvGLM+gPQTqjVVYSMUCfKW3EGXTEYaLHEl1bGduwqNC95O3bZflgtAb4L6w==
dependencies:
prosemirror-keymap "^1.0.0"
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-view "^1.0.0"
prosemirror-history@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.1.3.tgz#4f76a1e71db4ef7cdf0e13dec6d8da2aeaecd489"
integrity sha512-zGDotijea+vnfnyyUGyiy1wfOQhf0B/b6zYcCouBV8yo6JmrE9X23M5q7Nf/nATywEZbgRLG70R4DmfSTC+gfg==
dependencies:
prosemirror-state "^1.2.2"
prosemirror-transform "^1.0.0"
rope-sequence "^1.3.0"
prosemirror-inputrules@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.1.2.tgz#487e46c763e1212a4577397aba7706139084f012"
integrity sha512-Ja5Z3BWestlHYGvtSGqyvxMeB8QEuBjlHM8YnKtLGUXMDp965qdDV4goV8lJb17kIWHk7e7JNj6Catuoa3302g==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.4.tgz#8b481bf8389a5ac40d38dbd67ec3da2c7eac6a6d"
integrity sha512-Al8cVUOnDFL4gcI5IDlG6xbZ0aOD/i3B17VT+1JbHWDguCgt/lBHVTHUBcKvvbSg6+q/W4Nj1Fu6bwZSca3xjg==
dependencies:
prosemirror-state "^1.0.0"
w3c-keyname "^2.2.0"
prosemirror-markdown@^1.4.4:
version "1.5.0"
resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.5.0.tgz#1c0129640c33e23217fbf80ebb24486a40a759c1"
integrity sha512-ugTyZfTu1l2E3EI6+DwD917mz2Sk5E4R01Nh67yMffGg4S9ZetC81g1VIKGCaak4jnoP4BMUIOUJyXAS6xFLaA==
dependencies:
markdown-it "^10.0.0"
prosemirror-model "^1.0.0"
prosemirror-model@^1.0.0, prosemirror-model@^1.1.0, prosemirror-model@^1.10.0, prosemirror-model@^1.8.1:
version "1.11.0"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.11.0.tgz#dc36cdb3ad6442b9f6325c7d89170c624f9dc520"
integrity sha512-GqoAz/mIYjdv8gVYJ8mWFKpHoTxn/lXq4tXJ6bTVxs+rem2LzMYXrNVXfucGtfsgqsJlRIgng/ByG9j7Q8XDrg==
dependencies:
orderedmap "^1.1.0"
prosemirror-schema-list@^1.1.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.4.tgz#471f9caf2d2bed93641d2e490434c0d2d4330df1"
integrity sha512-pNTuZflacFOBlxrTcWSdWhjoB8BaucwfJVp/gJNxztOwaN3wQiC65axclXyplf6TKgXD/EkWfS/QAov3/Znadw==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.3.tgz#b2862866b14dec2b3ae1ab18229f2bd337651a2c"
integrity sha512-PLXh2VJsIgvlgSTH6I2Yg6vk1CzPDp21DFreVpQtDMY2S6WaMmrQgDTLRcsrD8X38v8Yc873H7+ogdGzyIPn+w==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-tables@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.1.1.tgz#ad66300cc49500455cf1243bb129c9e7d883321e"
integrity sha512-LmCz4jrlqQZRsYRDzCRYf/pQ5CUcSOyqZlAj5kv67ZWBH1SVLP2U9WJEvQfimWgeRlIz0y0PQVqO1arRm1+woA==
dependencies:
prosemirror-keymap "^1.1.2"
prosemirror-model "^1.8.1"
prosemirror-state "^1.3.1"
prosemirror-transform "^1.2.1"
prosemirror-view "^1.13.3"
prosemirror-transform@1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.2.5.tgz#7a3e2c61fcdbaf1d0844a2a3bc34fc3524e9809c"
integrity sha512-eqeIaxWtUfOnpA1ERrXCuSIMzqIJtL9Qrs5uJMCjY5RMSaH5o4pc390SAjn/IDPeIlw6auh0hCCXs3wRvGnQug==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1:
version "1.2.8"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.2.8.tgz#4b86544fa43637fe381549fb7b019f4fb71fe65c"
integrity sha512-hKqceqv9ZmMQXNQkhFjr0KFGPvkhygaWND+uIM0GxRpALrKfxP97SsgHTBs3OpJhDmh5N+mB4D/CksB291Eavg==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-utils@^0.9.6:
version "0.9.6"
resolved "https://registry.yarnpkg.com/prosemirror-utils/-/prosemirror-utils-0.9.6.tgz#3d97bd85897e3b535555867dc95a51399116a973"
integrity sha512-UC+j9hQQ1POYfMc5p7UFxBTptRiGPR7Kkmbl3jVvU8VgQbkI89tR/GK+3QYC8n+VvBZrtAoCrJItNhWSxX3slA==
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.14.11:
version "1.15.5"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.15.5.tgz#02f447bfb938995b6e26c9376c36d03e8714fcb4"
integrity sha512-VpAP/Rh7a4l1udpR3lEym2FlTD93JZjWChHYgzLgiBI6alOapG4GEsyAEq0l8eUJd9Ujf55+5dlEuItxI3a9Mw==
dependencies:
prosemirror-model "^1.1.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
proxy-addr@~2.0.5: proxy-addr@~2.0.5:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
@ -13639,6 +13805,11 @@ react-markdown@^4.3.1:
unist-util-visit "^1.3.0" unist-util-visit "^1.3.0"
xtend "^4.0.1" xtend "^4.0.1"
react-medium-image-zoom@^3.0.16:
version "3.1.2"
resolved "https://registry.yarnpkg.com/react-medium-image-zoom/-/react-medium-image-zoom-3.1.2.tgz#5ac4441f1d424bd9680a25bfc2591be3d7704a42"
integrity sha512-werjufn5o4ytdyvJNzfqXCilovDhMyREH0qeJhCjV5brNAyfV7anZmvpFc3FApbuVXwBkzHMuQkV2z/GyEQatg==
react-onclickoutside@^6.9.0: react-onclickoutside@^6.9.0:
version "6.9.0" version "6.9.0"
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz#a54bc317ae8cf6131a5d78acea55a11067f37a1f" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz#a54bc317ae8cf6131a5d78acea55a11067f37a1f"
@ -13665,6 +13836,13 @@ react-popper@^1.3.4, react-popper@^1.3.6:
typed-styles "^0.0.7" typed-styles "^0.0.7"
warning "^4.0.2" warning "^4.0.2"
react-portal@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.1.tgz#12c1599238c06fb08a9800f3070bea2a3f78b1a6"
integrity sha512-fE9kOBagwmTXZ3YGRYb4gcMy+kSA+yLO0xnPankjRlfBv4uCpFXqKPfkpsGQQR15wkZ9EssnvTOl1yMzbkxhPQ==
dependencies:
prop-types "^15.5.8"
react-redux@^7.1.1: react-redux@^7.1.1:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
@ -13990,7 +14168,7 @@ redux@^4.0.4:
loose-envify "^1.4.0" loose-envify "^1.4.0"
symbol-observable "^1.2.0" symbol-observable "^1.2.0"
refractor@^2.4.1: refractor@^2.10.1, refractor@^2.4.1:
version "2.10.1" version "2.10.1"
resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.10.1.tgz#166c32f114ed16fd96190ad21d5193d3afc7d34e" resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.10.1.tgz#166c32f114ed16fd96190ad21d5193d3afc7d34e"
integrity sha512-Xh9o7hQiQlDbxo5/XkOX6H+x/q8rmlmZKr97Ie1Q8ZM32IRRd3B/UxuA/yXDW79DBSXGWxm2yRTbcTVmAciJRw== integrity sha512-Xh9o7hQiQlDbxo5/XkOX6H+x/q8rmlmZKr97Ie1Q8ZM32IRRd3B/UxuA/yXDW79DBSXGWxm2yRTbcTVmAciJRw==
@ -14474,6 +14652,39 @@ rgba-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
rich-markdown-editor@^10.6.5:
version "10.6.5"
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-10.6.5.tgz#b74ae2e7d05eaa3c8ef34744e5cb0ed2dbdb0958"
integrity sha512-C/C+6L7BTXC4zSHgOYMljOQ3CvFt8zNCT829woKBHcDWSnXiUzpjgZZ4qEeNRlh/XJmqeFZYfqY+OzIMsVP2+Q==
dependencies:
copy-to-clipboard "^3.0.8"
lodash "^4.17.11"
markdown-it-container "^3.0.0"
markdown-it-mark "^3.0.0"
outline-icons "^1.21.0-3"
prismjs "^1.19.0"
prosemirror-commands "^1.1.4"
prosemirror-dropcursor "^1.3.2"
prosemirror-gapcursor "^1.1.5"
prosemirror-history "^1.1.3"
prosemirror-inputrules "^1.1.2"
prosemirror-keymap "^1.1.4"
prosemirror-markdown "^1.4.4"
prosemirror-model "^1.10.0"
prosemirror-schema-list "^1.1.2"
prosemirror-state "^1.3.3"
prosemirror-tables "^1.0.0"
prosemirror-transform "1.2.5"
prosemirror-utils "^0.9.6"
prosemirror-view "^1.14.11"
react-medium-image-zoom "^3.0.16"
react-portal "^4.2.1"
refractor "^2.10.1"
slugify "^1.4.0"
smooth-scroll-into-view-if-needed "^1.1.27"
styled-components "^5.1.0"
typescript "3.7.5"
right-align@^0.1.1: right-align@^0.1.1:
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@ -14503,6 +14714,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0" hash-base "^3.0.0"
inherits "^2.0.1" inherits "^2.0.1"
rope-sequence@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b"
integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==
rsvp@^4.8.4: rsvp@^4.8.4:
version "4.8.5" version "4.8.5"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
@ -14643,6 +14859,13 @@ schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6
ajv "^6.10.2" ajv "^6.10.2"
ajv-keywords "^3.4.1" ajv-keywords "^3.4.1"
scroll-into-view-if-needed@^2.2.25:
version "2.2.25"
resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.25.tgz#117b7bc7c61bc7a2b7872a0984bc73a19bc6e961"
integrity sha512-C8RKJPq9lK7eubwGpLbUkw3lklcG3Ndjmea2PyauzrA0i4DPlzAmVMGxaZrBFqCrVLfvJmP80IyHnv4jxvg1OQ==
dependencies:
compute-scroll-into-view "^1.0.14"
scuid@^1.0.2: scuid@^1.0.2:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab"
@ -14937,6 +15160,18 @@ slice-ansi@^2.1.0:
astral-regex "^1.0.0" astral-regex "^1.0.0"
is-fullwidth-code-point "^2.0.0" is-fullwidth-code-point "^2.0.0"
slugify@^1.4.0:
version "1.4.5"
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.5.tgz#a7517acf5f4c02a4df41e735354b660a4ed1efcf"
integrity sha512-WpECLAgYaxHoEAJ8Q1Lo8HOs1ngn7LN7QjXgOLbmmfkcWvosyk4ZTXkTzKyhngK640USTZUlgoQJfED1kz5fnQ==
smooth-scroll-into-view-if-needed@^1.1.27:
version "1.1.28"
resolved "https://registry.yarnpkg.com/smooth-scroll-into-view-if-needed/-/smooth-scroll-into-view-if-needed-1.1.28.tgz#70591d6723aa747114e0c0348379f8739701e4ba"
integrity sha512-VBxjAYySCfPix8JaiYKB+Rr4+1tFJ+aJ80caiVmsEA1cgQ3G/aFEfIRSpabJfX5o3FGm3IEttBLebe0uNtgURw==
dependencies:
scroll-into-view-if-needed "^2.2.25"
snapdragon-node@^2.0.1: snapdragon-node@^2.0.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@ -15479,6 +15714,22 @@ styled-components@^5.0.1:
shallowequal "^1.1.0" shallowequal "^1.1.0"
supports-color "^5.5.0" supports-color "^5.5.0"
styled-components@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.1.1.tgz#96dfb02a8025794960863b9e8e365e3b6be5518d"
integrity sha512-1ps8ZAYu2Husx+Vz8D+MvXwEwvMwFv+hqqUwhNlDN5ybg6A+3xyW1ECrAgywhvXapNfXiz79jJyU0x22z0FFTg==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/traverse" "^7.4.5"
"@emotion/is-prop-valid" "^0.8.8"
"@emotion/stylis" "^0.8.4"
"@emotion/unitless" "^0.7.4"
babel-plugin-styled-components ">= 1"
css-to-react-native "^3.0.0"
hoist-non-react-statics "^3.0.0"
shallowequal "^1.1.0"
supports-color "^5.5.0"
stylehacks@^4.0.0: stylehacks@^4.0.0:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
@ -15976,7 +16227,7 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@~3.7.2: typescript@3.7.5, typescript@~3.7.2:
version "3.7.5" version "3.7.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
@ -15986,6 +16237,11 @@ ua-parser-js@^0.7.18:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777"
integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uglify-js@^2.6.1: uglify-js@^2.6.1:
version "2.8.29" version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
@ -16519,6 +16775,11 @@ w3c-hr-time@^1.0.1:
dependencies: dependencies:
browser-process-hrtime "^0.1.2" browser-process-hrtime "^0.1.2"
w3c-keyname@^2.2.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b"
integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==
w3c-xmlserializer@^1.1.2: w3c-xmlserializer@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794"