Skip to content

Commit

Permalink
Merge pull request #789 from ionicprotocol/feat/markets-ui-update
Browse files Browse the repository at this point in the history
Markets UI update
  • Loading branch information
rhlsthrm authored Dec 5, 2024
2 parents b50b8db + b0b4f1e commit cfa8d39
Show file tree
Hide file tree
Showing 77 changed files with 7,070 additions and 5,474 deletions.
30 changes: 30 additions & 0 deletions packages/ui/app/_components/AnimateHeight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useState, useRef, useLayoutEffect } from 'react';

const AnimateHeight = ({ children }: { children: React.ReactNode }) => {
const [height, setHeight] = useState<number | null>(null);
const contentRef = useRef<HTMLDivElement>(null);

useLayoutEffect(() => {
if (contentRef.current) {
const resizeObserver = new ResizeObserver(() => {
if (contentRef.current) {
setHeight(contentRef.current.scrollHeight);
}
});

resizeObserver.observe(contentRef.current);
return () => resizeObserver.disconnect();
}
}, []);

return (
<div
className="transition-[height] duration-300 ease-in-out overflow-hidden"
style={{ height: height ? `${height}px` : 'auto' }}
>
<div ref={contentRef}>{children}</div>
</div>
);
};

export default AnimateHeight;
242 changes: 172 additions & 70 deletions packages/ui/app/_components/CommonTable.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import type { ReactNode } from 'react';
import { useState } from 'react';

import {
ArrowDownIcon,
ArrowUpIcon,
CaretSortIcon
} from '@radix-ui/react-icons';
import {
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable
useReactTable,
type ColumnDef,
type Row,
type SortingState
} from '@tanstack/react-table';

import {
Expand All @@ -16,98 +25,191 @@ import {
TableRow
} from '@ui/components/ui/table';

import { TableLoader } from './TableLoader';
import ResultHandler from './ResultHandler';

export const sortingFunctions = {
numerical: (a: any, b: any) => {
if (typeof a === 'number' && typeof b === 'number') {
return a - b;
}
const aValue = parseFloat(String(a).replace(/[^0-9.-]+/g, '')) || 0;
const bValue = parseFloat(String(b).replace(/[^0-9.-]+/g, '')) || 0;
return aValue - bValue;
},
alphabetical: (a: any, b: any) => String(a).localeCompare(String(b)),
percentage: (a: any, b: any) => {
const aValue = parseFloat(String(a).replace('%', '')) || 0;
const bValue = parseFloat(String(b).replace('%', '')) || 0;
return aValue - bValue;
}
};

type SortingType = keyof typeof sortingFunctions;

export type EnhancedColumnDef<T> = Omit<
ColumnDef<T, unknown>,
'header' | 'sortingFn'
> & {
id: string;
header: ReactNode | string;
sortingFn?: SortingType | ((rowA: any, rowB: any) => number);
enableSorting?: boolean;
accessorFn?: (row: T) => any;
};

interface RowBadge {
text: string;
className?: string;
}

import type { ColumnDef, SortingState } from '@tanstack/react-table';
interface RowStyle {
badge?: RowBadge;
borderClassName?: string;
}

interface CommonTableProps<T> {
interface CommonTableProps<T extends object> {
data: T[];
columns: ColumnDef<T>[];
columns: EnhancedColumnDef<T>[];
isLoading?: boolean;
loadingRows?: number;
showFooter?: boolean;
getRowStyle?: (row: Row<T>) => RowStyle;
}

function CommonTable<T>({
const SortableHeader = ({
column,
children
}: {
column: any;
children: ReactNode;
}) => {
const isSortable = column.getCanSort();
const sorted = column.getIsSorted();

const getSortIcon = () => {
if (!isSortable) return null;
if (sorted === 'asc') return <ArrowUpIcon className="w-4 h-4" />;
if (sorted === 'desc') return <ArrowDownIcon className="w-4 h-4" />;
return <CaretSortIcon className="w-4 h-4 text-white/40" />;
};

return (
<button
className={`flex items-center gap-2 ${!isSortable ? 'cursor-default' : 'hover:text-white'}`}
onClick={() => isSortable && column.toggleSorting(sorted === 'asc')}
disabled={!isSortable}
>
{children}
{getSortIcon()}
</button>
);
};

function CommonTable<T extends object>({
data,
columns,
isLoading = false,
loadingRows = 5,
showFooter = false
isLoading: externalIsLoading = false,
getRowStyle
}: CommonTableProps<T>) {
const [sorting, setSorting] = useState<SortingState>([]);
const [hasInitialized, setHasInitialized] = useState(false);

const isLoading = !hasInitialized || externalIsLoading;

if (!hasInitialized && data.length > 0) {
setHasInitialized(true);
}

const processedColumns = columns.map(
(col): ColumnDef<T> => ({
...col,
accessorFn: col.accessorFn || ((row: T) => (row as any)[col.id]),
header: ({ column }) => (
<SortableHeader column={column}>{col.header}</SortableHeader>
),
sortingFn:
typeof col.sortingFn === 'string'
? (rowA: any, rowB: any) => {
const sortFn = sortingFunctions[col.sortingFn as SortingType];
return sortFn(rowA.getValue(col.id), rowB.getValue(col.id));
}
: col.sortingFn,
enableSorting: col.enableSorting !== false
})
);

const table = useReactTable({
data,
columns,
columns: processedColumns,
getCoreRowModel: getCoreRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
state: {
sorting
}
state: { sorting },
enableMultiSort: false
});

if (isLoading) {
return (
<TableLoader
columns={columns}
rows={loadingRows}
showFooter={showFooter}
/>
);
}

return (
<Table className="w-full border-separate border-spacing-y-3">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={headerGroup.id}
className="border-none hover:bg-transparent"
>
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
className="text-white/60 text-xs font-semibold h-8"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<ResultHandler
isLoading={isLoading}
center
height={80}
>
<Table className="pr-3.5 pl-[1px]">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={row.id}
className="hover:bg-graylite transition-all duration-200 ease-linear bg-grayUnselect rounded-xl [&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl border-none"
key={headerGroup.id}
transparent
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
className="text-white/60 text-xs font-semibold h-8"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
))}
</TableHeader>
<TableBody>
{!isLoading && table.getRowModel().rows?.length === 0 ? (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
) : (
table.getRowModel().rows.map((row) => {
const rowStyle = getRowStyle ? getRowStyle(row) : {};

return (
<TableRow
key={row.id}
badge={rowStyle.badge}
borderClassName={rowStyle.borderClassName}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
);
})
)}
</TableBody>
</Table>
</ResultHandler>
);
}

Expand Down
58 changes: 0 additions & 58 deletions packages/ui/app/_components/Modal.tsx

This file was deleted.

Loading

0 comments on commit cfa8d39

Please sign in to comment.