Skip to content

Commit

Permalink
Show Data Skeleton Loading (#5328)
Browse files Browse the repository at this point in the history
### Description

Show Data Skeleton loading

### Refs

#4460

### Demo

Figma:
https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty?type=design&node-id=25429-70096&mode=design&t=VRxtgYCKnJkl2zpt-0

https://jam.dev/c/178878cb-e600-4370-94d5-c8c12c8fe0d5

Fixes #4460

---------

Co-authored-by: gitstart-twenty <[email protected]>
Co-authored-by: v1b3m <[email protected]>
Co-authored-by: Matheus <[email protected]>
  • Loading branch information
4 people authored May 14, 2024
1 parent de438b0 commit 1bc9b78
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';

Expand Down Expand Up @@ -27,15 +29,84 @@ const StyledMainContainer = styled.div`
justify-content: center;
`;

const StyledSkeletonContainer = styled.div`
align-items: center;
width: 100%;
padding: ${({ theme }) => theme.spacing(8)};
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
flex-wrap: wrap;
align-content: flex-start;
`;

const StyledSkeletonSubSection = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing(4)};
`;

const StyledSkeletonColumn = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
justify-content: center;
`;

const StyledSkeletonLoader = () => {
const theme = useTheme();
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={80}
>
<Skeleton width={24} height={84} />
</SkeletonTheme>
);
};

const StyledTimelineSkeletonLoader = () => {
const theme = useTheme();
const skeletonItems = Array.from({ length: 3 }).map((_, index) => ({
id: `skeleton-item-${index}`,
}));
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<StyledSkeletonContainer>
<Skeleton width={440} height={16} />
{skeletonItems.map(({ id }) => (
<StyledSkeletonSubSection key={id}>
<StyledSkeletonLoader />
<StyledSkeletonColumn>
<Skeleton width={400} height={24} />
<Skeleton width={400} height={24} />
</StyledSkeletonColumn>
</StyledSkeletonSubSection>
))}
</StyledSkeletonContainer>
</SkeletonTheme>
);
};

export const Timeline = ({
targetableObject,
loading,
}: {
targetableObject: ActivityTargetableObject;
loading?: boolean;
}) => {
const timelineActivitiesForGroup = useRecoilValue(
timelineActivitiesForGroupState,
);

if (loading === true) {
return <StyledTimelineSkeletonLoader />;
}

if (timelineActivitiesForGroup.length === 0) {
return (
<AnimatedPlaceholderEmptyContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ import { RecordInlineCellContainer } from './RecordInlineCellContainer';

type RecordInlineCellProps = {
readonly?: boolean;
loading?: boolean;
};

export const RecordInlineCell = ({ readonly }: RecordInlineCellProps) => {
export const RecordInlineCell = ({
readonly,
loading,
}: RecordInlineCellProps) => {
const { fieldDefinition, entityId } = useContext(FieldContext);

const buttonIcon = useGetButtonIcon();
Expand Down Expand Up @@ -99,6 +103,7 @@ export const RecordInlineCell = ({ readonly }: RecordInlineCellProps) => {
isDisplayModeContentEmpty={isFieldEmpty}
isDisplayModeFixHeight
editModeContentOnly={isFieldInputOnly}
loading={loading}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { Tooltip } from 'react-tooltip';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
Expand Down Expand Up @@ -82,6 +83,25 @@ const StyledTooltip = styled(Tooltip)`
padding: ${({ theme }) => theme.spacing(2)};
`;

const StyledSkeletonDiv = styled.div`
height: 24px;
`;

const StyledInlineCellSkeletonLoader = () => {
const theme = useTheme();
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<StyledSkeletonDiv>
<Skeleton width={154} height={16} />
</StyledSkeletonDiv>
</SkeletonTheme>
);
};

type RecordInlineCellContainerProps = {
readonly?: boolean;
IconLabel?: IconComponent;
Expand All @@ -96,6 +116,7 @@ type RecordInlineCellContainerProps = {
isDisplayModeContentEmpty?: boolean;
isDisplayModeFixHeight?: boolean;
disableHoverEffect?: boolean;
loading?: boolean;
};

export const RecordInlineCellContainer = ({
Expand All @@ -112,6 +133,7 @@ export const RecordInlineCellContainer = ({
editModeContentOnly,
isDisplayModeFixHeight,
disableHoverEffect,
loading = false,
}: RecordInlineCellContainerProps) => {
const { entityId, fieldDefinition } = useContext(FieldContext);
const reference = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -163,6 +185,43 @@ export const RecordInlineCellContainer = ({
}
}, [isHoveredForDisplayMode, displayModeContent, reference]);

const showContent = () => {
if (loading) {
return <StyledInlineCellSkeletonLoader />;
}
return !readonly && isInlineCellInEditMode ? (
<RecordInlineCellEditMode>{editModeContent}</RecordInlineCellEditMode>
) : editModeContentOnly ? (
<StyledClickableContainer readonly={readonly}>
<RecordInlineCellDisplayMode
disableHoverEffect={disableHoverEffect}
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isHovered}
emptyPlaceholder={showLabel ? 'Empty' : label}
>
{editModeContent}
</RecordInlineCellDisplayMode>
</StyledClickableContainer>
) : (
<StyledClickableContainer
readonly={readonly}
onClick={handleDisplayModeClick}
>
<RecordInlineCellDisplayMode
disableHoverEffect={disableHoverEffect}
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isHovered}
emptyPlaceholder={showLabel ? 'Empty' : label}
>
{newDisplayModeContent}
</RecordInlineCellDisplayMode>
{showEditButton && <RecordInlineCellButton Icon={buttonIcon} />}
</StyledClickableContainer>
);
};

return (
<StyledInlineCellBaseContainer
onMouseEnter={handleContainerMouseEnter}
Expand Down Expand Up @@ -194,37 +253,7 @@ export const RecordInlineCellContainer = ({
</StyledLabelAndIconContainer>
)}
<StyledValueContainer ref={reference}>
{!readonly && isInlineCellInEditMode ? (
<RecordInlineCellEditMode>{editModeContent}</RecordInlineCellEditMode>
) : editModeContentOnly ? (
<StyledClickableContainer readonly={readonly}>
<RecordInlineCellDisplayMode
disableHoverEffect={disableHoverEffect}
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isHovered}
emptyPlaceholder={showLabel ? 'Empty' : label}
>
{editModeContent}
</RecordInlineCellDisplayMode>
</StyledClickableContainer>
) : (
<StyledClickableContainer
readonly={readonly}
onClick={handleDisplayModeClick}
>
<RecordInlineCellDisplayMode
disableHoverEffect={disableHoverEffect}
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isHovered}
emptyPlaceholder={showLabel ? 'Empty' : label}
>
{newDisplayModeContent}
</RecordInlineCellDisplayMode>
{showEditButton && <RecordInlineCellButton Icon={buttonIcon} />}
</StyledClickableContainer>
)}
{showContent()}
</StyledValueContainer>
</StyledInlineCellBaseContainer>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
type RecordShowContainerProps = {
objectNameSingular: string;
objectRecordId: string;
loading: boolean;
};

export const RecordShowContainer = ({
objectNameSingular,
objectRecordId,
loading,
}: RecordShowContainerProps) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
Expand Down Expand Up @@ -130,13 +132,14 @@ export const RecordShowContainer = ({
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>
<ShowPageContainer>
<ShowPageLeftContainer>
{!recordLoading && isDefined(recordFromStore) && (
{isDefined(recordFromStore) && (
<>
<ShowPageSummaryCard
id={objectRecordId}
logoOrAvatar={recordIdentifier?.avatarUrl ?? ''}
avatarPlaceholder={recordIdentifier?.name ?? ''}
date={recordFromStore.createdAt ?? ''}
loading={loading || recordLoading}
title={
<FieldContext.Provider
value={{
Expand Down Expand Up @@ -193,7 +196,10 @@ export const RecordShowContainer = ({
hotkeyScope: InlineCellHotkeyScope.InlineCell,
}}
>
<RecordInlineCell readonly={isReadOnly} />
<RecordInlineCell
loading={loading || recordLoading}
readonly={isReadOnly}
/>
</FieldContext.Provider>
))}
</PropertyBox>
Expand All @@ -217,7 +223,9 @@ export const RecordShowContainer = ({
hotkeyScope: InlineCellHotkeyScope.InlineCell,
}}
>
<RecordDetailRelationSection />
<RecordDetailRelationSection
loading={loading || recordLoading}
/>
</FieldContext.Provider>
))}
</>
Expand All @@ -233,6 +241,7 @@ export const RecordShowContainer = ({
tasks
notes
emails
loading={loading || recordLoading}
/>
) : (
<></>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useCallback, useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import qs from 'qs';
import { useRecoilValue } from 'recoil';
Expand Down Expand Up @@ -27,11 +29,36 @@ import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';

type RecordDetailRelationSectionProps = {
loading: boolean;
};

const StyledAddDropdown = styled(Dropdown)`
margin-left: auto;
`;

export const RecordDetailRelationSection = () => {
const StyledSkeletonDiv = styled.div`
height: 40px;
`;

const StyledRecordDetailRelationSectionSkeletonLoader = () => {
const theme = useTheme();
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<StyledSkeletonDiv>
<Skeleton width={129} height={16} />
</StyledSkeletonDiv>
</SkeletonTheme>
);
};

export const RecordDetailRelationSection = ({
loading,
}: RecordDetailRelationSectionProps) => {
const { entityId, fieldDefinition } = useContext(FieldContext);
const {
fieldName,
Expand Down Expand Up @@ -113,6 +140,20 @@ export const RecordDetailRelationSection = () => {
relationObjectMetadataItem.namePlural
}?${qs.stringify(filterQueryParams)}`;

const showContent = () => {
if (loading) {
return <StyledRecordDetailRelationSectionSkeletonLoader />;
}

return relationRecords.length ? (
<RecordDetailRelationRecordsList relationRecords={relationRecords} />
) : (
<RecordDetailRelationRecordsListEmptyState
relationObjectMetadataItem={relationObjectMetadataItem}
/>
);
};

return (
<RecordDetailSection>
<RecordDetailSectionHeader
Expand Down Expand Up @@ -159,13 +200,7 @@ export const RecordDetailRelationSection = () => {
</DropdownScope>
}
/>
{relationRecords.length ? (
<RecordDetailRelationRecordsList relationRecords={relationRecords} />
) : (
<RecordDetailRelationRecordsListEmptyState
relationObjectMetadataItem={relationObjectMetadataItem}
/>
)}
{showContent()}
</RecordDetailSection>
);
};
Loading

0 comments on commit 1bc9b78

Please sign in to comment.