Add "show company / people" view and "Notes" concept (#528)

* Begin adding show view and refactoring threads to become notes

* Progress on design

* Progress redesign timeline

* Dropdown button, design improvement

* Open comment thread edit mode in drawer

* Autosave local storage and commentThreadcount

* Improve display and fix missing key issue

* Remove some hardcoded CSS properties

* Create button

* Split company show into ui/business + fix eslint

* Fix font weight

* Begin auto-save on edit mode

* Save server-side query result to Apollo cache

* Fix save behavior

* Refetch timeline after creating note

* Rename createCommentThreadWithComment

* Improve styling

* Revert "Improve styling"

This reverts commit 9fbbf2db006e529330edc64f3eb8ff9ecdde6bb0.

* Improve CSS styling

* Bring back border radius inadvertently removed

* padding adjustment

* Improve blocknote design

* Improve edit mode display

* Remove Comments.tsx

* Remove irrelevant comment stories

* Removed un-necessary panel component

* stop using fragment, move trash icon

* Add a basic story for CompanyShow

* Add a basic People show view

* Fix storybook tests

* Add very basic Person story

* Refactor PR1

* Refactor part 2

* Refactor part 3

* Refactor part 4

* Fix tests

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Félix Malfait
2023-07-10 07:25:34 +02:00
committed by GitHub
parent ca180acf9f
commit 94a913a41f
191 changed files with 5390 additions and 721 deletions

View File

@ -0,0 +1,94 @@
import React, { useState } from 'react';
import styled from '@emotion/styled';
import { IconChevronDown } from '@/ui/icons/index';
type ButtonProps = React.ComponentProps<'button'>;
export type DropdownOptionType = {
label: string;
icon: React.ReactNode;
};
type Props = {
options: DropdownOptionType[];
onSelection: (value: DropdownOptionType) => void;
} & ButtonProps;
const StyledButton = styled.button<ButtonProps>`
align-items: center;
background: ${({ theme }) => theme.background.tertiary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: 4px;
color: ${({ theme }) => theme.font.color.secondary};
display: flex;
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
gap: 8px;
height: 24px;
line-height: ${({ theme }) => theme.text.lineHeight.lg};
padding: 3px 8px;
svg {
align-items: center;
display: flex;
height: 14px;
justify-content: center;
width: 14px;
}
`;
const DropdownContainer = styled.div`
position: relative;
`;
const DropdownMenu = styled.div`
display: flex;
flex-direction: column;
margin-top: -2px;
position: absolute;
`;
export function DropdownButton({
options,
onSelection,
...buttonProps
}: Props) {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState(options[0]);
if (!options.length) {
throw new Error('You must provide at least one option.');
}
const handleSelect =
(option: DropdownOptionType) =>
(event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
onSelection(option);
setSelectedOption(option);
setIsOpen(false);
};
return (
<DropdownContainer>
<StyledButton onClick={() => setIsOpen(!isOpen)} {...buttonProps}>
{selectedOption.icon}
{selectedOption.label}
{options.length > 1 && <IconChevronDown />}
</StyledButton>
{isOpen && (
<DropdownMenu>
{options
.filter((option) => option.label !== selectedOption.label)
.map((option, index) => (
<StyledButton key={index} onClick={handleSelect(option)}>
{option.icon}
{option.label}
</StyledButton>
))}
</DropdownMenu>
)}
</DropdownContainer>
);
}

View File

@ -6,17 +6,19 @@ import { textInputStyle } from '@/ui/themes/effects';
import { EditableCell } from '../EditableCell';
export type EditableChipProps = {
id: string;
placeholder?: string;
value: string;
picture: string;
changeHandler: (updated: string) => void;
editModeHorizontalAlign?: 'left' | 'right';
ChipComponent: ComponentType<{
id: string;
name: string;
picture: string;
isOverlapped?: boolean;
}>;
commentCount?: number;
commentThreadCount?: number;
onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
rightEndContents?: ReactNode[];
};
@ -41,6 +43,7 @@ const RightContainer = styled.div`
// TODO: move right end content in EditableCell
export function EditableCellChip({
id,
value,
placeholder,
changeHandler,
@ -75,7 +78,7 @@ export function EditableCellChip({
}
nonEditModeContent={
<NoEditModeContainer>
<ChipComponent name={inputValue} picture={picture} />
<ChipComponent id={id} name={inputValue} picture={picture} />
<RightContainer>
{rightEndContents &&
rightEndContents.length > 0 &&

View File

@ -0,0 +1,28 @@
import { BlockNoteEditor } from '@blocknote/core';
import { BlockNoteView } from '@blocknote/react';
import styled from '@emotion/styled';
interface BlockEditorProps {
editor: BlockNoteEditor | null;
}
const StyledEditor = styled.div`
min-height: 200px;
width: 100%;
& .editor-create-mode,
.editor-edit-mode {
background: ${({ theme }) => theme.background.primary};
}
& .editor-create-mode [class^='_inlineContent']:before {
color: ${({ theme }) => theme.font.color.tertiary};
font-style: normal !important;
}
`;
export function BlockEditor({ editor }: BlockEditorProps) {
return (
<StyledEditor>
<BlockNoteView editor={editor} />
</StyledEditor>
);
}

View File

@ -22,7 +22,7 @@ const StyledContainer = styled.div`
font-size: ${({ theme }) => theme.font.size.md};
border: none;
display: block;
font-weight: 400;
font-weight: ${({ theme }) => theme.font.weight.regular};
}
& .react-datepicker-popper {

View File

@ -27,8 +27,8 @@ const StyledTextArea = styled(TextareaAutosize)`
border-radius: 5px;
color: ${({ theme }) => theme.font.color.primary};
font-family: inherit;
font-size: 13px;
font-weight: 400;
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
line-height: 16px;
overflow: auto;
padding: 8px;
@ -42,7 +42,7 @@ const StyledTextArea = styled(TextareaAutosize)`
&::placeholder {
color: ${({ theme }) => theme.font.color.light};
font-weight: 400;
font-weight: ${({ theme }) => theme.font.weight.regular};
}
`;
@ -121,7 +121,7 @@ export function AutosizeTextInput({
<>
<StyledContainer>
<StyledTextArea
placeholder={placeholder || 'Write something...'}
placeholder={placeholder || 'Write a comment'}
maxRows={MAX_ROWS}
minRows={computedMinRows}
onChange={handleInputChange}

View File

@ -0,0 +1,21 @@
import styled from '@emotion/styled';
const StyledPropertyBoxContainer = styled.div`
align-self: stretch;
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: 4px;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)} ${({ theme }) => theme.spacing(3)};
`;
interface PropertyBoxProps {
children: JSX.Element;
extraPadding?: boolean;
}
export function PropertyBox({ children }: PropertyBoxProps) {
return <StyledPropertyBoxContainer>{children}</StyledPropertyBoxContainer>;
}

View File

@ -0,0 +1,56 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
const StyledPropertyBoxItem = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
`;
const StyledIconContainer = styled.div`
align-items: center;
display: flex;
svg {
align-items: center;
display: flex;
height: 16px;
justify-content: center;
width: 16px;
}
`;
const StyledValueContainer = styled.div`
align-content: flex-start;
align-items: center;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex: 1 0 0;
flex-wrap: wrap;
`;
const StyledLabelAndIconContainer = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.tertiary};
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
`;
export function PropertyBoxItem({
icon,
label,
value,
}: {
icon: ReactNode;
label?: string;
value: ReactNode;
}) {
return (
<StyledPropertyBoxItem>
<StyledLabelAndIconContainer>
<StyledIconContainer>{icon}</StyledIconContainer>
{label}
</StyledLabelAndIconContainer>
<StyledValueContainer>{value}</StyledValueContainer>
</StyledPropertyBoxItem>
);
}

View File

@ -9,7 +9,7 @@ const StyledMainSectionTitle = styled.h2`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.xl};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
line-height: 1.5;
line-height: ${({ theme }) => theme.text.lineHeight.lg};
margin: 0;
`;

View File

@ -10,7 +10,7 @@ const StyledTitle = styled.div`
align-items: center;
display: flex;
flex-direction: row;
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
height: ${({ theme }) => theme.spacing(8)};
padding-left: ${({ theme }) => theme.spacing(2)};
`;

View File

@ -32,7 +32,7 @@ const StyledButton = styled.div<StyledButtonProps>`
`;
const StyledButtonLabel = styled.div`
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-left: ${({ theme }) => theme.spacing(2)};
`;

View File

@ -1,4 +1,4 @@
import { IconComment } from '@/ui/icons/index';
import { IconNotes } from '@/ui/icons/index';
import { EntityTableActionBarButton } from './EntityTableActionBarButton';
@ -9,8 +9,8 @@ type OwnProps = {
export function TableActionBarButtonToggleComments({ onClick }: OwnProps) {
return (
<EntityTableActionBarButton
label="Comment"
icon={<IconComment size={16} />}
label="Notes"
icon={<IconNotes size={16} />}
onClick={onClick}
/>
);

View File

@ -179,7 +179,7 @@ function DropdownButton({
};
const dropdownRef = useRef(null);
useOutsideAlerter(dropdownRef, onOutsideClick);
useOutsideAlerter({ ref: dropdownRef, callback: onOutsideClick });
return (
<StyledDropdownButtonContainer>

View File

@ -43,7 +43,7 @@ const StyledCancelButton = styled.button`
border: none;
color: ${({ theme }) => theme.font.color.secondary};
cursor: pointer;
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-left: auto;
margin-right: ${({ theme }) => theme.spacing(2)};
padding: ${(props) => {

View File

@ -45,7 +45,7 @@ const StyledDelete = styled.div`
`;
const StyledLabelKey = styled.div`
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
`;
function SortOrFilterChip({

View File

@ -27,7 +27,7 @@ const StyledTableHeader = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
display: flex;
flex-direction: row;
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
height: 40px;
justify-content: space-between;
padding-left: ${({ theme }) => theme.spacing(3)};
@ -49,7 +49,7 @@ const StyledViewSection = styled.div`
const StyledFilters = styled.div`
display: flex;
font-weight: 400;
font-weight: ${({ theme }) => theme.font.weight.regular};
gap: 2px;
`;

View File

@ -7,7 +7,7 @@ const onOutsideClick = jest.fn();
function TestComponent() {
const buttonRef = useRef(null);
useOutsideAlerter(buttonRef, onOutsideClick);
useOutsideAlerter({ ref: buttonRef, callback: onOutsideClick });
return (
<div>

View File

@ -1,21 +1,52 @@
import { useEffect } from 'react';
declare type CallbackType = () => void;
export enum OutsideClickAlerterMode {
absolute = 'absolute',
dom = 'dom',
}
export function useOutsideAlerter(
ref: React.RefObject<HTMLInputElement>,
callback: CallbackType,
) {
type OwnProps = {
ref: React.RefObject<HTMLInputElement>;
callback: () => void;
mode?: OutsideClickAlerterMode;
};
export function useOutsideAlerter({
ref,
mode = OutsideClickAlerterMode.dom,
callback,
}: OwnProps) {
useEffect(() => {
function handleClickOutside(event: Event) {
function handleClickOutside(event: MouseEvent) {
const target = event.target as HTMLButtonElement;
if (ref.current && !ref.current.contains(target)) {
if (!ref.current) {
return;
}
if (
mode === OutsideClickAlerterMode.dom &&
!ref.current.contains(target)
) {
callback();
}
if (mode === OutsideClickAlerterMode.absolute) {
const { x, y, width, height } = ref.current.getBoundingClientRect();
const { clientX, clientY } = event;
if (
clientX < x ||
clientX > x + width ||
clientY < y ||
clientY > y + height
) {
callback();
}
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref, callback]);
}, [ref, callback, mode]);
}

View File

@ -30,3 +30,6 @@ export { IconArrowUpRight } from '@tabler/icons-react';
export { IconBrandGoogle } from '@tabler/icons-react';
export { IconUpload } from '@tabler/icons-react';
export { IconFileUpload } from '@tabler/icons-react';
export { IconChevronsRight } from '@tabler/icons-react';
export { IconNotes } from '@tabler/icons-react';
export { IconCirclePlus } from '@tabler/icons-react';

View File

@ -10,7 +10,7 @@ type OwnProps = {
topMargin?: number;
};
const MainContainer = styled.div<{ topMargin: number }>`
const StyledMainContainer = styled.div<{ topMargin: number }>`
background: ${({ theme }) => theme.background.noisy};
display: flex;
@ -27,27 +27,21 @@ type LeftContainerProps = {
isRightDrawerOpen?: boolean;
};
const LeftContainer = styled.div<LeftContainerProps>`
const StyledLeftContainer = styled.div<LeftContainerProps>`
display: flex;
position: relative;
width: calc(
100% -
${(props) =>
props.isRightDrawerOpen
? `${props.theme.rightDrawerWidth} - ${props.theme.spacing(2)}`
: '0px'}
);
width: 100%;
`;
export function ContentContainer({ children, topMargin }: OwnProps) {
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
return (
<MainContainer topMargin={topMargin ?? 0}>
<LeftContainer isRightDrawerOpen={isRightDrawerOpen}>
<StyledMainContainer topMargin={topMargin ?? 0}>
<StyledLeftContainer isRightDrawerOpen={isRightDrawerOpen}>
<Panel>{children}</Panel>
</LeftContainer>
</StyledLeftContainer>
<RightDrawer />
</MainContainer>
</StyledMainContainer>
);
}

View File

@ -8,7 +8,7 @@ const StyledTitle = styled.div`
color: ${({ theme }) => theme.font.color.light};
display: flex;
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: 600;
font-weight: ${({ theme }) => theme.font.weight.semiBold};
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(1)};
padding-top: ${({ theme }) => theme.spacing(8)};

View File

@ -42,7 +42,7 @@ const StyledName = styled.div`
color: ${({ theme }) => theme.font.color.primary};
font-family: 'Inter';
font-size: ${({ theme }) => theme.font.size.md};
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-left: ${({ theme }) => theme.spacing(2)};
`;

View File

@ -1,33 +1,61 @@
import { useRef } from 'react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import {
OutsideClickAlerterMode,
useOutsideAlerter,
} from '@/ui/hooks/useOutsideAlerter';
import { isDefined } from '@/utils/type-guards/isDefined';
import { Panel } from '../../Panel';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerRouter } from './RightDrawerRouter';
const StyledContainer = styled.div`
background: ${({ theme }) => theme.background.primary};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
height: 100%;
overflow-x: hidden;
position: fixed;
right: 0;
top: 0;
transition: width 0.5s;
width: ${({ theme }) => theme.rightDrawerWidth};
z-index: 2;
`;
const StyledRightDrawer = styled.div`
display: flex;
flex-direction: row;
width: ${({ theme }) => theme.rightDrawerWidth};
width: 100%;
`;
export function RightDrawer() {
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const [isRightDrawerOpen, setIsRightDrawerOpen] = useRecoilState(
isRightDrawerOpenState,
);
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
const rightDrawerRef = useRef(null);
useOutsideAlerter({
ref: rightDrawerRef,
callback: () => setIsRightDrawerOpen(false),
mode: OutsideClickAlerterMode.absolute,
});
if (!isRightDrawerOpen || !isDefined(rightDrawerPage)) {
return <></>;
}
return (
<StyledRightDrawer>
<Panel>
<RightDrawerRouter />
</Panel>
</StyledRightDrawer>
<>
<StyledContainer>
<StyledRightDrawer ref={rightDrawerRef}>
<RightDrawerRouter />
</StyledRightDrawer>
</StyledContainer>
</>
);
}

View File

@ -3,5 +3,7 @@ import styled from '@emotion/styled';
export const RightDrawerBody = styled.div`
display: flex;
flex-direction: column;
height: calc(100vh - ${({ theme }) => theme.spacing(10)});
overflow: auto;
position: relative;
`;

View File

@ -1,10 +1,12 @@
import { useRecoilState } from 'recoil';
import { RightDrawerComments } from '@/comments/components/RightDrawerComments';
import { RightDrawerCreateCommentThread } from '@/comments/components/RightDrawerCreateCommentThread';
import { RightDrawerCreateCommentThread } from '@/comments/components/right-drawer/create/RightDrawerCreateCommentThread';
import { RightDrawerEditCommentThread } from '@/comments/components/right-drawer/edit/RightDrawerEditCommentThread';
import { RightDrawerTimeline } from '@/comments/components/right-drawer/RightDrawerTimeline';
import { isDefined } from '@/utils/type-guards/isDefined';
import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerPages } from '../types/RightDrawerPages';
export function RightDrawerRouter() {
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
@ -13,11 +15,14 @@ export function RightDrawerRouter() {
return <></>;
}
return rightDrawerPage === 'comments' ? (
<RightDrawerComments />
) : rightDrawerPage === 'create-comment-thread' ? (
<RightDrawerCreateCommentThread />
) : (
<></>
);
switch (rightDrawerPage) {
case RightDrawerPages.Timeline:
return <RightDrawerTimeline />;
case RightDrawerPages.CreateCommentThread:
return <RightDrawerCreateCommentThread />;
case RightDrawerPages.EditCommentThread:
return <RightDrawerEditCommentThread />;
default:
return <></>;
}
}

View File

@ -1,5 +1,7 @@
import styled from '@emotion/styled';
import { Button } from '@/ui/components/buttons/Button';
import { RightDrawerTopBarCloseButton } from './RightDrawerTopBarCloseButton';
const StyledRightDrawerTopBar = styled.div`
@ -8,7 +10,7 @@ const StyledRightDrawerTopBar = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
display: flex;
flex-direction: row;
font-size: 13px;
font-size: ${({ theme }) => theme.font.size.md};
justify-content: space-between;
min-height: 40px;
padding-left: 8px;
@ -17,19 +19,24 @@ const StyledRightDrawerTopBar = styled.div`
const StyledTopBarTitle = styled.div`
align-items: center;
font-weight: 500;
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-right: ${({ theme }) => theme.spacing(1)};
`;
export function RightDrawerTopBar({
title,
}: {
type OwnProps = {
title: string | null | undefined;
}) {
onSave?: () => void;
};
export function RightDrawerTopBar({ title, onSave }: OwnProps) {
function handleOnClick() {
onSave?.();
}
return (
<StyledRightDrawerTopBar>
<StyledTopBarTitle>{title}</StyledTopBarTitle>
<RightDrawerTopBarCloseButton />
<StyledTopBarTitle>{title}</StyledTopBarTitle>
{onSave && <Button title="Save" onClick={handleOnClick} />}
</StyledRightDrawerTopBar>
);
}

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { IconPlus } from '@/ui/icons/index';
import { IconChevronsRight } from '@/ui/icons/index';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
@ -24,7 +24,6 @@ const StyledButton = styled.button`
}
svg {
color: ${({ theme }) => theme.font.color.tertiary};
transform: rotate(45deg);
}
`;
@ -37,7 +36,7 @@ export function RightDrawerTopBarCloseButton() {
return (
<StyledButton onClick={handleButtonClick}>
<IconPlus size={16} />
<IconChevronsRight size={16} />
</StyledButton>
);
}

View File

@ -2,13 +2,13 @@ import { useRecoilState } from 'recoil';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerPage } from '../types/RightDrawerPage';
import { RightDrawerPages } from '../types/RightDrawerPages';
export function useOpenRightDrawer() {
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const [, setRightDrawerPage] = useRecoilState(rightDrawerPageState);
return function openRightDrawer(rightDrawerPage: RightDrawerPage) {
return function openRightDrawer(rightDrawerPage: RightDrawerPages) {
setRightDrawerPage(rightDrawerPage);
setIsRightDrawerOpen(true);
};

View File

@ -1,8 +1,8 @@
import { atom } from 'recoil';
import { RightDrawerPage } from '../types/RightDrawerPage';
import { RightDrawerPages } from '../types/RightDrawerPages';
export const rightDrawerPageState = atom<RightDrawerPage | null>({
export const rightDrawerPageState = atom<RightDrawerPages | null>({
key: 'ui/layout/right-drawer-page',
default: null,
});

View File

@ -1 +0,0 @@
export type RightDrawerPage = 'comments' | 'create-comment-thread';

View File

@ -0,0 +1,5 @@
export enum RightDrawerPages {
Timeline = 'timeline',
CreateCommentThread = 'create-comment-thread',
EditCommentThread = 'edit-comment-thread',
}

View File

@ -0,0 +1,85 @@
import { Tooltip } from 'react-tooltip';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { v4 as uuidV4 } from 'uuid';
import { Avatar } from '@/users/components/Avatar';
import {
beautifyExactDate,
beautifyPastDateRelativeToNow,
} from '@/utils/datetime/date-utils';
const StyledShowPageSummaryCard = styled.div`
align-items: center;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
justify-content: center;
padding: ${({ theme }) => theme.spacing(6)} ${({ theme }) => theme.spacing(3)}
${({ theme }) => theme.spacing(3)} ${({ theme }) => theme.spacing(3)};
`;
const StyledInfoContainer = styled.div`
align-items: center;
display: flex;
flex-direction: column;
`;
const StyledDate = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
cursor: pointer;
`;
const StyledTitle = styled.div`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.xl};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
`;
const StyledTooltip = styled(Tooltip)`
background-color: ${({ theme }) => theme.background.primary};
box-shadow: ${({ theme }) => theme.boxShadow.light};
color: ${({ theme }) => theme.font.color.primary};
padding: ${({ theme }) => theme.spacing(2)};
`;
export function ShowPageSummaryCard({
logoOrAvatar,
title,
date,
}: {
logoOrAvatar?: string;
title: string;
date: string;
}) {
const beautifiedCreatedAt =
date !== '' ? beautifyPastDateRelativeToNow(date) : '';
const exactCreatedAt = date !== '' ? beautifyExactDate(date) : '';
const theme = useTheme();
const dateElementId = `date-id-${uuidV4()}`;
return (
<StyledShowPageSummaryCard>
<Avatar
avatarUrl={logoOrAvatar}
size={theme.icon.size.xl}
placeholder={title}
/>
<StyledInfoContainer>
<StyledTitle>{title}</StyledTitle>
<StyledDate id={dateElementId}>
Added {beautifiedCreatedAt} ago
</StyledDate>
<StyledTooltip
anchorSelect={`#${dateElementId}`}
content={exactCreatedAt}
clickable
noArrow
place="right"
/>
</StyledInfoContainer>
</StyledShowPageSummaryCard>
);
}

View File

@ -0,0 +1,9 @@
import styled from '@emotion/styled';
export const ShowPageLeftContainer = styled.div`
background: ${({ theme }) => theme.background.secondary};
border-radius: 8px;
border-right: 1px solid ${({ theme }) => theme.border.color.light};
padding: 0px ${({ theme }) => theme.spacing(3)};
width: 320px;
`;

View File

@ -0,0 +1,9 @@
import styled from '@emotion/styled';
export const ShowPageRightContainer = styled.div`
display: flex;
flex: 1 0 0;
flex-direction: column;
justify-content: center;
overflow: hidden;
`;

View File

@ -3,6 +3,7 @@ export const icon = {
sm: 14,
md: 16,
lg: 20,
xl: 40,
},
stroke: {
sm: 1.6,

View File

@ -18,7 +18,7 @@ const common = {
horizontalCellMargin: '8px',
checkboxColumnWidth: '32px',
},
rightDrawerWidth: '300px',
rightDrawerWidth: '500px',
clickableElementBackgroundTransition: 'background 0.1s ease',
lastLayerZIndex: 2147483647,
};
@ -32,6 +32,7 @@ export const lightTheme = {
selectedCardHover: color.blue20,
selectedCard: color.blue10,
font: fontLight,
name: 'light',
},
};
export type ThemeType = typeof lightTheme;
@ -45,6 +46,7 @@ export const darkTheme: ThemeType = {
selectedCardHover: color.blue70,
selectedCard: color.blue80,
font: fontDark,
name: 'dark',
},
};