Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/large-teams-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@venusprotocol/ui": minor
---

add Markets table to Markets page
1 change: 1 addition & 0 deletions apps/evm/src/components/Icon/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,5 @@ export { default as user } from './user';
export { default as expand } from './expand';
export { default as dollar } from './dollar';
export { default as gift } from './gift';
export { default as pendingOutline } from './pendingOutline';
export { default as crown } from './crown';
28 changes: 28 additions & 0 deletions apps/evm/src/components/Icon/icons/pendingOutline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { SVGProps } from 'react';

const SvgPendingOutline = (props: SVGProps<SVGSVGElement>) => (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect x="0.5" y="0.5" width="15" height="15" rx="7.5" stroke="currentColor" />
<path
d="M4 9C4.55228 9 5 8.55228 5 8C5 7.44772 4.55228 7 4 7C3.44772 7 3 7.44772 3 8C3 8.55228 3.44772 9 4 9Z"
fill="currentColor"
/>
<path
d="M8 9C8.55228 9 9 8.55228 9 8C9 7.44772 8.55228 7 8 7C7.44772 7 7 7.44772 7 8C7 8.55228 7.44772 9 8 9Z"
fill="currentColor"
/>
<path
d="M12 9C12.5523 9 13 8.55228 13 8C13 7.44772 12.5523 7 12 7C11.4477 7 11 7.44772 11 8C11 8.55228 11.4477 9 12 9Z"
fill="currentColor"
/>
</svg>
);

export default SvgPendingOutline;
2 changes: 1 addition & 1 deletion apps/evm/src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const Modal: React.FC<ModalProps> = ({
<Fade in={isOpen}>
<div
className={cn(
'overflow-auto outline-hidden bg-cards rounded-xl absolute top-[50%] left-[50%] translate-y-[-50%] translate-x-[-50%] border border-lightGrey max-w-136 w-[calc(100%-2rem)] max-h-[calc(100%-2rem)]',
'overflow-auto outline-hidden bg-dark-blue rounded-xl absolute top-[50%] left-[50%] translate-y-[-50%] translate-x-[-50%] border border-blue max-w-136 w-[calc(100%-2rem)] max-h-[calc(100%-2rem)]',
className,
)}
>
Expand Down
54 changes: 28 additions & 26 deletions apps/evm/src/components/Modal/styles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { css } from '@emotion/react';
import { useTheme } from '@mui/material';
import { theme } from '@venusprotocol/ui';

export const useModalStyles = ({
hasTitleComponent,
Expand All @@ -8,27 +9,28 @@ export const useModalStyles = ({
hasTitleComponent: boolean;
noHorizontalPadding?: boolean;
}) => {
const theme = useTheme();
const muiTheme = useTheme();

return {
titleWrapper: css`
padding: ${theme.spacing(6, 6, hasTitleComponent ? 6 : 0, 6)};
border-bottom: ${hasTitleComponent ? `1px solid ${theme.palette.secondary.light}` : 0};
padding: ${muiTheme.spacing(6, 6, hasTitleComponent ? 6 : 0, 6)};
position: sticky;
top: 0;
z-index: 10;
background-color: ${hasTitleComponent ? theme.palette.background.paper : 'transparent'};
margin-bottom: ${hasTitleComponent ? theme.spacing(6) : 0};
background-color: ${hasTitleComponent ? theme.colors['dark-blue'] : 'transparent'};
margin-bottom: ${hasTitleComponent ? muiTheme.spacing(6) : 0};
display: flex;
align-items: center;
justify-content: center;
${theme.breakpoints.down('md')} {
margin-bottom: ${hasTitleComponent ? theme.spacing(4) : 0};
border-top-left-radius: ${muiTheme.spacing(4)};
border-top-right-radius: ${muiTheme.spacing(4)};
${muiTheme.breakpoints.down('md')} {
margin-bottom: ${hasTitleComponent ? muiTheme.spacing(4) : 0};
}
`,
backAction: css`
position: absolute;
left: ${theme.spacing(6)};
left: ${muiTheme.spacing(6)};
padding: 0;
min-width: auto;
background-color: transparent;
Expand All @@ -39,27 +41,27 @@ export const useModalStyles = ({
`,
backArrow: css`
transform: rotate(180deg);
height: ${theme.shape.iconSize.xLarge}px;
width: ${theme.shape.iconSize.xLarge}px;
color: ${theme.palette.text.primary};
height: ${muiTheme.shape.iconSize.xLarge}px;
width: ${muiTheme.shape.iconSize.xLarge}px;
color: ${muiTheme.palette.text.primary};
`,
titleComponent: css`
align-self: center;
display: flex;
justify-content: center;
align-items: center;
min-height: ${theme.shape.iconSize.xLarge}px;
padding-left: ${theme.shape.iconSize.xLarge}px;
padding-right: ${theme.shape.iconSize.xLarge}px;
font-size: ${theme.typography.h4.fontSize};
font-weight: ${theme.typography.h4.fontWeight};
min-height: ${muiTheme.shape.iconSize.xLarge}px;
padding-left: ${muiTheme.shape.iconSize.xLarge}px;
padding-right: ${muiTheme.shape.iconSize.xLarge}px;
font-size: ${muiTheme.typography.h4.fontSize};
font-weight: ${muiTheme.typography.h4.fontWeight};
`,
closeIcon: css`
top: 50%;
margin-top: ${-theme.shape.iconSize.xLarge / 2}px;
margin-top: ${-muiTheme.shape.iconSize.xLarge / 2}px;
position: absolute;
height: ${theme.shape.iconSize.xLarge}px;
width: ${theme.shape.iconSize.xLarge}px;
height: ${muiTheme.shape.iconSize.xLarge}px;
width: ${muiTheme.shape.iconSize.xLarge}px;
margin-left: auto;
min-width: 0;
padding: 0;
Expand All @@ -70,13 +72,13 @@ export const useModalStyles = ({
}
`,
contentWrapper: css`
padding-bottom: ${theme.spacing(6)};
padding-left: ${noHorizontalPadding ? 0 : theme.spacing(6)};
padding-right: ${noHorizontalPadding ? 0 : theme.spacing(6)};
${theme.breakpoints.down('md')} {
padding-bottom: ${theme.spacing(4)};
padding-left: ${noHorizontalPadding ? 0 : theme.spacing(4)};
padding-right: ${noHorizontalPadding ? 0 : theme.spacing(4)};
padding-bottom: ${muiTheme.spacing(6)};
padding-left: ${noHorizontalPadding ? 0 : muiTheme.spacing(6)};
padding-right: ${noHorizontalPadding ? 0 : muiTheme.spacing(6)};
${muiTheme.breakpoints.down('md')} {
padding-bottom: ${muiTheme.spacing(4)};
padding-left: ${noHorizontalPadding ? 0 : muiTheme.spacing(4)};
padding-right: ${noHorizontalPadding ? 0 : muiTheme.spacing(4)};
}
`,
};
Expand Down
9 changes: 7 additions & 2 deletions apps/evm/src/components/Table/Head.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import MuiTableRow from '@mui/material/TableRow';
import MuiTableSortLabel from '@mui/material/TableSortLabel';
import { visuallyHidden } from '@mui/utils';

import { cn } from '@venusprotocol/ui';
import { Icon } from '../Icon';
import { useStyles } from './styles';
import type { TableColumn } from './types';
Expand All @@ -16,6 +17,7 @@ interface HeadProps<R> {
orderDirection: 'asc' | 'desc' | undefined;
onRequestOrder: (column: TableColumn<R>) => void;
controls: boolean;
rowControlColumn: boolean;
className?: string;
}

Expand All @@ -26,11 +28,12 @@ function Head<R>({
onRequestOrder,
className,
controls,
rowControlColumn,
}: HeadProps<R>) {
const styles = useStyles();
return (
<MuiTableHead>
<MuiTableRow className={className}>
<MuiTableHead className={cn('h-14', className)}>
<MuiTableRow>
{columns.map(column => {
const active = orderBy?.key === column.key;

Expand Down Expand Up @@ -79,6 +82,8 @@ function Head<R>({
</MuiTableCell>
);
})}

{rowControlColumn && <MuiTableCell className="w-8" />}
</MuiTableRow>
</MuiTableHead>
);
Expand Down
15 changes: 15 additions & 0 deletions apps/evm/src/components/Table/RowControl/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { cn } from '@venusprotocol/ui';

import { Icon } from 'components/Icon';

export interface RowControlProps extends React.HTMLAttributes<HTMLButtonElement> {}

export const RowControl: React.FC<RowControlProps> = ({ className, ...otherProps }) => (
<button
type="button"
className={cn('p-2 -mr-2 cursor-pointer text-light-grey hover:text-white', className)}
{...otherProps}
>
<Icon name="pendingOutline" className="text-inherit transition-colors" />
</button>
);
73 changes: 73 additions & 0 deletions apps/evm/src/components/Table/TableCards/MarketCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { cn } from '@venusprotocol/ui';
import type { To } from 'react-router';

import { Card } from 'components/Card';
import { Delimiter } from 'components/Delimiter';
import { LabeledInlineContent } from 'components/LabeledInlineContent';
import { RowControl } from 'components/Table/RowControl';
import type { TableProps } from 'components/Table/types';
import { Link } from 'containers/Link';

export interface MarketCardProps<R> {
row: R;
index: number;
columns: TableProps<R>['columns'];
key: string;
href?: To;
onClick?: TableProps<R>['rowOnClick'];
onControlClick?: TableProps<R>['rowControlOnClick'];
className?: string;
}

export function MarketCard<R>({
index,
row,
href,
columns,
onClick,
onControlClick,
className,
key,
}: MarketCardProps<R>) {
const [titleColumn, ...otherColumns] = columns;

const content = (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>{titleColumn.renderCell(row, index)}</div>

{onControlClick && <RowControl onClick={e => onControlClick?.(e, row)} />}
</div>

<Delimiter className="my-4" />

<div className="space-y-6">
{otherColumns.map(column => (
<LabeledInlineContent key={`${key}-${column.key}`} label={column.label}>
<div className="text-right inline-flex">{column.renderCell(row, index)}</div>
</LabeledInlineContent>
))}
</div>
</div>
);

return (
<Card
className={cn(
!!(onClick || href) && 'cursor-pointer hover:bg-dark-blue-hover active:bg-dark-blue-active',
className,
)}
asChild
onClick={e => onClick?.(e, row)}
key={key}
>
{href ? (
<Link className="text-white no-underline hover:no-underline" noStyle to={href}>
{content}
</Link>
) : (
<div>{content}</div>
)}
</Card>
);
}
94 changes: 94 additions & 0 deletions apps/evm/src/components/Table/TableCards/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { useMemo } from 'react';

import { Spinner, cn } from '@venusprotocol/ui';
import { useTranslation } from 'libs/translations';

import { Select, type SelectOption, type SelectProps } from 'components/Select';
import type { TableCardProps } from '../types';
import { MarketCard } from './MarketCard';

export function TableCards<R>({
cardClassName,
data,
isFetching,
rowKeyExtractor,
rowOnClick,
getRowHref,
breakpoint,
columns,
order,
onOrderChange,
showMobileFilter = true,
rowControlOnClick,
selectVariant = 'tertiary',
}: TableCardProps<R>) {
const { t } = useTranslation();

const selectOptions = useMemo(
() =>
columns.reduce((acc, column) => {
if (!column.sortRows) {
return acc;
}

const option: SelectOption = {
value: column.key,
label: column.selectOptionLabel,
};

return [...acc, option];
}, [] as SelectOption[]),
[columns],
);

const selectedOption = useMemo(
() => order && selectOptions.find(option => option.value === order.orderBy.key),
[order, selectOptions],
);

const handleOrderChange: SelectProps['onChange'] = value => {
const newSelectedOption = selectOptions.find(option => option.value === value);
const orderBy =
newSelectedOption && columns.find(column => column.key === newSelectedOption.value);

if (orderBy) {
onOrderChange({
orderBy,
orderDirection: 'desc',
});
}
};

return (
<div className={cn(!breakpoint && 'hidden', breakpoint && `block ${breakpoint}:hidden`)}>
{showMobileFilter && selectOptions.length > 0 && (
<Select
label={t('table.cardsSelect.label')}
placeLabelToLeft
options={selectOptions}
value={selectedOption?.value || selectOptions[0].value}
onChange={handleOrderChange}
className="mb-4 w-56"
variant={selectVariant}
/>
)}

{isFetching && <Spinner className="mb-5" />}

<div className="space-y-3">
{data.map((row, rowIndex) => (
<MarketCard
key={rowKeyExtractor(row)}
index={rowIndex}
row={row}
onClick={rowOnClick}
onControlClick={rowControlOnClick}
className={cardClassName}
columns={columns}
href={getRowHref?.(row)}
/>
))}
</div>
</div>
);
}
Loading