Commit 8bfb04b2 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #83 from blockscout/skeletons

skeletons
parents 0c9eb30e 9225e72c
export default function delay(time: number) {
return new Promise((resolve) => window.setTimeout(resolve, time));
}
import { Skeleton as SkeletonComponent } from '@chakra-ui/react';
import { keyframes } from '@chakra-ui/system';
import type { SystemStyleFunction } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools';
const shine = () =>
keyframes({
to: { backgroundPositionX: '-200%' },
});
const baseStyle: SystemStyleFunction = (props) => {
const defaultStartColor = mode('blackAlpha.50', 'whiteAlpha.50')(props);
const defaultEndColor = mode('blackAlpha.100', 'whiteAlpha.100')(props);
const {
startColor = defaultStartColor,
endColor = defaultEndColor,
speed,
theme,
} = props;
const start = getColor(theme, startColor);
const end = getColor(theme, endColor);
return {
opacity: 1,
borderRadius: 'base',
borderColor: start,
background: `linear-gradient(90deg, ${ start } 8%, ${ end } 18%, ${ start } 33%)`,
backgroundSize: '200% 100%',
animation: `${ speed }s linear infinite ${ shine() }`,
};
};
const Skeleton = {
baseStyle,
};
export default Skeleton;
SkeletonComponent.defaultProps = {
...SkeletonComponent.defaultProps,
speed: 1,
};
......@@ -7,6 +7,7 @@ import Link from './Link';
import Modal from './Modal';
import Popover from './Popover';
import Radio from './Radio';
import Skeleton from './Skeleton';
import Table from './Table';
import Tabs from './Tabs';
import Tag from './Tag';
......@@ -24,6 +25,7 @@ const components = {
Modal,
Popover,
Radio,
Skeleton,
Tabs,
Table,
Tag,
......
......@@ -18,7 +18,7 @@ interface Props {
onDeleteClick: (item: ApiKey) => void;
}
const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
const ApiKeyTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
const onItemEditClick = useCallback(() => {
return onEditClick(item);
......@@ -47,4 +47,4 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
);
};
export default WatchlistTableItem;
export default ApiKeyTableItem;
......@@ -22,8 +22,7 @@ import styles from './ColorModeToggler.module.css';
export interface ColorModeTogglerProps
extends Omit<UseCheckboxProps, 'isIndeterminate'>,
Omit<HTMLChakraProps<'label'>, keyof UseCheckboxProps>,
ThemingProps<'Switch'> {
}
ThemingProps<'Switch'> {}
const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((props, ref) => {
const ownProps = omitThemingProps(props);
......
import { Box, Button, HStack, Link, Text, Spinner, useDisclosure } from '@chakra-ui/react';
import { Box, Button, HStack, Link, Text, Skeleton, useDisclosure } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
......@@ -10,6 +10,7 @@ import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page/Page';
import SkeletonTable from 'ui/shared/SkeletonTable';
const DATA_LIMIT = 3;
......@@ -50,20 +51,23 @@ const ApiKeysPage: React.FC = () => {
const content = (() => {
if (isLoading || isError) {
return <Spinner/>;
return (
<>
<SkeletonTable columns={ [ '100%', '108px' ] }/>
<Skeleton height="44px" width="156px" marginTop={ 8 }/>
</>
);
}
const canAdd = data.length < DATA_LIMIT;
return (
<>
{ data.length > 0 && (
<ApiKeyTable
data={ data }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
limit={ DATA_LIMIT }
/>
) }
<ApiKeyTable
data={ data }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
limit={ DATA_LIMIT }
/>
<HStack marginTop={ 8 } spacing={ 5 }>
<Button
variant="primary"
......
import { Box, Button, Spinner, Text, useDisclosure } from '@chakra-ui/react';
import { Box, Button, Text, Skeleton, useDisclosure } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react';
import type { AddressTags, AddressTag } from 'types/api/account';
import SkeletonTable from 'ui/shared/SkeletonTable';
import AddressModal from './AddressModal/AddressModal';
import AddressTagTable from './AddressTagTable/AddressTagTable';
import DeletePrivateTagModal from './DeletePrivateTagModal';
type Props = {
addressTags: AddressTags;
addressTags?: AddressTags;
}
const PrivateAddressTags = ({ addressTags }: Props) => {
......@@ -38,13 +40,26 @@ const PrivateAddressTags = ({ addressTags }: Props) => {
deleteModalProps.onClose();
}, [ deleteModalProps ]);
return (
<>
<Text marginBottom={ 12 }>
const description = (
<Text marginBottom={ 12 }>
Use private transaction tags to label any transactions of interest.
Private tags are saved in your account and are only visible when you are logged in.
</Text>
{ !addressTags && <Spinner/> }
</Text>
);
if (!addressTags) {
return (
<>
{ description }
<SkeletonTable columns={ [ '60%', '40%', '108px' ] }/>
<Skeleton height="44px" width="156px" marginTop={ 8 }/>
</>
);
}
return (
<>
{ description }
{ Boolean(addressTags?.length) && (
<AddressTagTable
data={ addressTags }
......
import { Box, Button, Text, useDisclosure } from '@chakra-ui/react';
import { Box, Button, Skeleton, Text, useDisclosure } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react';
import type { TransactionTags, TransactionTag } from 'types/api/account';
import SkeletonTable from 'ui/shared/SkeletonTable';
import DeletePrivateTagModal from './DeletePrivateTagModal';
import TransactionModal from './TransactionModal/TransactionModal';
import TransactionTagTable from './TransactionTagTable/TransactionTagTable';
type Props = {
transactionTags: TransactionTags;
transactionTags?: TransactionTags;
}
const PrivateTransactionTags = ({ transactionTags }: Props) => {
......@@ -38,12 +40,26 @@ const PrivateTransactionTags = ({ transactionTags }: Props) => {
deleteModalProps.onClose();
}, [ deleteModalProps ]);
return (
<>
<Text marginBottom={ 12 }>
const description = (
<Text marginBottom={ 12 }>
Use private transaction tags to label any transactions of interest.
Private tags are saved in your account and are only visible when you are logged in.
</Text>
</Text>
);
if (!transactionTags) {
return (
<>
{ description }
<SkeletonTable columns={ [ '75%', '25%', '108px' ] }/>
<Skeleton height="44px" width="156px" marginTop={ 8 }/>
</>
);
}
return (
<>
{ description }
{ Boolean(transactionTags.length) && (
<TransactionTagTable
data={ transactionTags }
......
import { Box, Text } from '@chakra-ui/react';
import { keyframes } from '@chakra-ui/system';
import React from 'react';
const runnerAnimation = keyframes`
0% { left: 0%; transform: translateX(-1%); }
100% { left: '100%'; transform: translateX(-99%); }
`;
const ContentLoader = () => {
return (
<Box display="inline-block">
<Box
width="100%"
height="6px"
position="relative"
_after={{
content: `" "`,
position: 'absolute',
width: '60px',
height: '6px',
animation: `${ runnerAnimation } 700ms ease-in-out infinite alternate`,
left: '100%',
top: 0,
backgroundColor: 'blue.300',
borderRadius: 'full',
}}
/>
<Text mt={ 6 } variant="secondary">Loading data, please wait... </Text>
</Box>
);
};
export default ContentLoader;
import { HStack, Skeleton } from '@chakra-ui/react';
import React from 'react';
interface Props {
columns: Array<string>;
}
const SkeletonTable = ({ columns }: Props) => {
return (
<div>
<Skeleton height={ 10 } width="100%" borderBottomLeftRadius="none" borderBottomRightRadius="none"/>
{ Array.from(Array(3)).map((item, index) => (
<HStack key={ index } spacing={ 6 } marginTop={ 8 }>
{ columns.map((width, index) => (
<Skeleton
key={ index }
height={ 5 }
width={ width }
flexShrink={ width.includes('%') ? 'initial' : 0 }
borderRadius="full"
/>
)) }
</HStack>
)) }
</div>
);
};
export default React.memo(SkeletonTable);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment